Ejemplo n.º 1
0
 class Meta:
     model = GeographicRegion
     fields = tuple([
         'id', 'name', 'slug', 'code', 'hidden', 'level', 'geom', 'site',
         'centroid', 'envelope', 'parent', 'parent__name',
         'languages_available'
     ] + generate_translated_fields('title'))
Ejemplo n.º 2
0
 class Meta:
     model = Provider
     fields = tuple([
         'url',
         'id',
     ] + generate_translated_fields('name') +
                    generate_translated_fields('description') +
                    generate_translated_fields('focal_point_name') +
                    generate_translated_fields('address') + [
                        'type', 'phone_number', 'website', 'facebook',
                        'twitter', 'focal_point_phone_number', 'user',
                        'number_of_monthly_beneficiaries', 'is_frozen',
                        'service_types', 'meta_population', 'record',
                        'requirement', 'vacancy', 'additional_info'
                    ])
     required_translated_fields = [
         'name', 'description', 'focal_point_name', 'address'
     ]
Ejemplo n.º 3
0
    def test_translated_fields(self):
        # Test getters and setters for each language in the settings
        nationality, _ = Nationality.objects.get_or_create(number=1)

        name_fields = generate_translated_fields('name', False)
        for field in name_fields:
            setattr(nationality, field, field)
        nationality.save()

        for field in name_fields:
            self.assertEqual(getattr(nationality, field), field)
Ejemplo n.º 4
0
 def test_provider_change_own_data(self):
     # Non-staff can change their own provider
     provider = ProviderFactory(user=self.user)
     name_fields = generate_translated_fields('name', False)
     # Tweak some data
     for field in name_fields:
         setattr(provider, field, random_string(10))
     book = get_export_workbook([provider])
     rsp = self.import_book(book)
     self.assertEqual(OK, rsp.status_code, msg=rsp.content.decode('utf-8'))
     new_provider = Provider.objects.get(id=provider.id)
     for field in name_fields:
         self.assertEqual(getattr(provider, field),
                          getattr(new_provider, field))
Ejemplo n.º 5
0
 def run_validation(self, data=serializers.empty):
     # data is a dictionary
     errs = defaultdict(list)
     for field in self.Meta.required_translated_fields:
         if not any(
                 data.get(key, False)
                 for key in generate_translated_fields(field, False)):
             errs[field].append(_('This field is required.'))
     try:
         validated_data = super().run_validation(data)
     except (exceptions.ValidationError, DjangoValidationError) as exc:
         errs.update(serializers.get_validation_error_detail(exc))
     if errs:
         raise exceptions.ValidationError(errs)
     return validated_data
Ejemplo n.º 6
0
 def test_provider_bad_criteria(self):
     provider = ProviderFactory(user=self.user)
     service = ServiceFactory(provider=provider,
                              status=Service.STATUS_CURRENT)
     criterion1 = SelectionCriterionFactory(service=service)
     criterion2 = SelectionCriterionFactory(service=service)
     # Change the 2nd one's text before exporting
     for field in generate_translated_fields('text', False):
         setattr(criterion2, field, '')
     book = get_export_workbook([provider], None, [criterion1, criterion2])
     rsp = self.import_book(book)
     self.assertContains(
         rsp,
         "Selection criterion must have text in at least one language",
         status_code=BAD_REQUEST,
         msg_prefix=rsp.content.decode('utf-8'))
Ejemplo n.º 7
0
 def test_staff_change_provider_invalid_id(self):
     self.user.is_staff = True
     self.user.save()
     provider = ProviderFactory()
     name_fields = generate_translated_fields('name', False)
     # Tweak some data
     for field in name_fields:
         setattr(provider, field, random_string(10))
     book = get_export_workbook([provider], cell_overwrite_ok=True)
     sheet = book.get_sheet(0)
     sheet.write(r=1, c=0, label='xyz')
     rsp = self.import_book(book)
     self.assertContains(rsp,
                         "id: xyz is not a valid ID",
                         status_code=BAD_REQUEST,
                         msg_prefix=rsp.content.decode('utf-8'))
Ejemplo n.º 8
0
 def test_staff_change_provider(self):
     # Staff can change another user's provider
     self.user.is_staff = True
     self.user.save()
     provider = ProviderFactory()
     name_fields = generate_translated_fields('name', False)
     # Tweak some data
     for field in name_fields:
         setattr(provider, field, random_string(10))
     book = get_export_workbook([provider])
     rsp = self.import_book(book)
     self.assertEqual(OK, rsp.status_code, msg=rsp.content.decode('utf-8'))
     new_provider = Provider.objects.get(id=provider.id)
     for field in name_fields:
         self.assertEqual(getattr(provider, field),
                          getattr(new_provider, field))
Ejemplo n.º 9
0
 def test_staff_change_nonexistent_provider(self):
     # Staff can change another user's provider
     self.user.is_staff = True
     self.user.save()
     provider = ProviderFactory()
     name_fields = generate_translated_fields('name', False)
     # Tweak some data
     for field in name_fields:
         setattr(provider, field, random_string(10))
     book = get_export_workbook([provider])
     provider_id = provider.id
     provider.delete()
     rsp = self.import_book(book)
     self.assertContains(rsp,
                         "There is no provider with id=%d" % provider_id,
                         status_code=BAD_REQUEST,
                         msg_prefix=rsp.content.decode('utf-8'))
Ejemplo n.º 10
0
 def test_staff_change_providers(self):
     # Staff can change multiple providers
     self.user.is_staff = True
     self.user.save()
     provider1 = ProviderFactory()
     provider2 = ProviderFactory()
     name_fields = generate_translated_fields('name', False)
     # Tweak some data
     for field in name_fields:
         setattr(provider1, field, random_string(10))
     provider2.number_of_monthly_beneficiaries = 1024
     provider2.type = ProviderTypeFactory()
     book = get_export_workbook([provider1, provider2])
     rsp = self.import_book(book)
     self.assertEqual(OK, rsp.status_code, msg=rsp.content.decode('utf-8'))
     new_provider1 = Provider.objects.get(id=provider1.id)
     for field in name_fields:
         self.assertEqual(getattr(provider1, field),
                          getattr(new_provider1, field))
     new_provider2 = Provider.objects.get(id=provider2.id)
     self.assertEqual(provider2.number_of_monthly_beneficiaries,
                      new_provider2.number_of_monthly_beneficiaries)
Ejemplo n.º 11
0
 def test_approval_validation(self):
     service = ServiceFactory(location=None)
     # No location - should not allow approval
     try:
         service.validate_for_approval()
     except ValidationError as e:
         self.assertIn('location', e.error_dict)
     else:
         self.fail("Should have gotten ValidationError")
     # Add location, should be okay
     service.location = 'POINT(5 23)'
     service.validate_for_approval()
     # No name, should fail
     name_fields = generate_translated_fields('name', False)
     for field in name_fields:
         setattr(service, field, '')
     try:
         service.validate_for_approval()
     except ValidationError as e:
         self.assertIn('name', e.error_dict)
     else:
         self.fail("Should have gotten ValidationError")
Ejemplo n.º 12
0
class PrivateServiceViewSet(FilterByRegionMixin, viewsets.ModelViewSet):
    filter_class = PrivateServiceFilter
    queryset = Service.objects.select_related(
        'provider',
        'type',
        'region',
    ).prefetch_related('selection_criteria', 'tags', 'types',
                       'contact_information').all()

    serializer_class = serializers_v2.ServiceSerializer
    pagination_class = StandardResultsSetPagination
    filter_backends = (django_filters.DjangoFilterBackend,
                       filters.OrderingFilter, SearchFilter)
    search_fields = ['name'] + generate_translated_fields('title', False)

    def get_queryset(self):
        qs = super(PrivateServiceViewSet, self).get_queryset()
        if not (hasattr(self.request, 'user')
                and self.request.user.is_superuser):
            providers = self.request.user.all_providers
            qs = qs.filter(
                Q(status__in=[Service.STATUS_CURRENT]) |
                Q(status__in=[Service.STATUS_PRIVATE], provider__in=providers))
        return qs
Ejemplo n.º 13
0
class SelectionCriterionInlineAdmin(admin.TabularInline):
    model = SelectionCriterion
    fields = generate_translated_fields('text', False)
Ejemplo n.º 14
0
class GeographicRegionViewSet(viewsets.ModelViewSet):
    permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
    queryset = GeographicRegion.objects.select_related('parent').all()
    serializer_class = serializers_v2.GeographicRegionSerializer
    pagination_class = StandardResultsSetPagination
    filter_class = GeographicRegionFilter
    search_fields = ['name'] + generate_translated_fields('title', False)

    def get_serializer_class(self):
        if getattr(self, 'action') == 'create':
            return serializers_v2.GeographicRegionCreateSerializer
        if ('exclude_geometry' in self.request.GET
                or 'countries' in self.request.GET):
            return serializers_v2.GeographicRegionSerializerNoGeometry
        else:
            return serializers_v2.GeographicRegionSerializer

    def get_queryset(self):
        qs = super(GeographicRegionViewSet, self).get_queryset()
        if 'countries' in self.request.GET:
            qs = qs.filter(Q(level=1) & Q(hidden=False))
        if (hasattr(self.request, 'parent')):
            qs = qs.filter(
                Q(parent=self.request.parent)
                | Q(parent__parent=self.request.parent))

        return qs

    def update(self, request, *args, **kwargs):
        instance = self.get_object()
        # serializer with geom for return only
        serializerReturn = self.get_serializer(instance,
                                               data=request.data,
                                               partial=True)
        serializerReturn.is_valid(raise_exception=True)
        data = serializerReturn.data

        geom = instance.geom.ewkt.split(";")[1]
        geomobj = instance.geom
        instance.geom = None
        request.data.pop('geom')
        region = kwargs.pop('pk')
        # serializer without geom objet to save
        serializer = self.get_serializer(instance,
                                         data=request.data,
                                         partial=True)
        serializer.is_valid(raise_exception=True)
        TypesOrdering.objects.filter(region=region).delete()
        types_ordering = request.data['types_ordering']
        for i, obj in enumerate(types_ordering):
            t = TypesOrdering(ordering=i,
                              region_id=region,
                              service_type_id=obj['id'])
            t.save()

        self.perform_update(serializer)
        cursor = connections['default'].cursor()
        cursor.execute(
            "update regions_geographicregion set geom = ST_GEOMFROMTEXT(%s, 4326) where id = %s ;",
            [geom, region])
        self.geom = geomobj
        return Response(data)
Ejemplo n.º 15
0
from django.db import transaction
from django.utils.translation import ugettext as _

from xlrd import open_workbook, XLRDError
import xlwt

from api.utils import generate_translated_fields
from services.forms import ProviderForm, ServiceForm, SelectionCriterionForm
from services.models import Service, SelectionCriterion, Provider

logger = logging.getLogger(__name__)

PROVIDER_SHEET_NAME = "Providers"
PROVIDER_HEADINGS = [
    'id',
] + generate_translated_fields('name', False) + [
    'type__number',
] + generate_translated_fields('type__name', False) + [
    'phone_number',
    'website',
] + generate_translated_fields('description', False) \
    + generate_translated_fields('focal_point_name', False) + [
        'focal_point_phone_number'
    ] + generate_translated_fields('address', False) + [
        'email',
        'number_of_monthly_beneficiaries',
        'password'
    ]

SERVICES_SHEET_NAME = 'Services'
SERVICE_HEADINGS = [
Ejemplo n.º 16
0
 class Meta:
     model = ServiceArea
     fields = tuple(['id', 'parent', 'url'] +
                    generate_translated_fields('name'))
     required_translated_fields = ['name']
Ejemplo n.º 17
0
class ProviderViewSet(FilterByRegionMixin, viewsets.ModelViewSet):
    # queryset = Provider.objects.prefetch_related('services').all()
    queryset = Provider.objects.all()
    serializer_class = serializers_v2.ProviderSerializer
    pagination_class = StandardResultsSetPagination

    # All the text fields that are used for full-text searches (?search=XXXXX)
    search_fields = generate_translated_fields('name', False) \
        + generate_translated_fields('description', False) \
        + generate_translated_fields('focal_point_name', False) \
        + generate_translated_fields('address', False) \
        + generate_translated_fields('type__name', False) \
        + ['phone_number'] \
        + ['website'] \
        + ['number_of_monthly_beneficiaries'] \
        + ['focal_point_phone_number']

    def update(self, request, *args, **kwargs):
        """On change to provider via the API, notify via JIRA"""
        response = super().update(request, *args, **kwargs)
        provider = Provider.objects.get(id=request.data["id"])
        if request.data["is_frozen"]:
            """ Delete all tokens and sessions for users linked to provider """
            users = [
                user
                for user in list([provider.user]) + list(provider.team.all())
                if user
            ]
            Token.objects.filter(user__in=users).delete()
            for user in users:
                user_sessions = []
                all_sessions = Session.objects.filter(
                    expire_date__gte=timezone.now())
                for session in all_sessions:
                    session_data = session.get_decoded()
                    if str(user.pk) == session_data.get('_auth_user_id'):
                        user_sessions.append(session.pk)
                Session.objects.filter(pk__in=user_sessions).delete()
        return response

    @list_route(methods=['get'], permission_classes=[IsAuthenticated])
    def my_providers(self, request):
        filtered = self.filter_queryset(self.get_queryset())
        my = filtered.filter(user=request.user) | request.user.providers.all()

        return Response([
            self.get_serializer_class()(a, context={
                'request': request
            }).data for a in my
        ])

    @list_route(methods=['post'], permission_classes=[AllowAny])
    def create_provider(self, request, *args, **kwargs):
        """
        Customized "create provider" API call.

        This is distinct from the built-in 'POST to the list URL'
        call because we need it to work for users who are not
        authenticated (otherwise, they can't register).

        Expected data is basically the same as for creating a provider,
        except that in place of the 'user' field, there should be an
        'email' and 'password' field.  They'll be used to create a new user,
        send them an activation email, and create a provider using
        that user.
        """
        with atomic(
        ):  # If we throw an exception anywhere in here, rollback all changes
            serializer = CreateProviderSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)

            # Create User
            user = get_user_model().objects.create_user(
                email=request.data['email'],
                password=request.data['password'],
                is_active=False)
            provider_group, _ = Group.objects.get_or_create(name='Providers')
            user.groups.add(provider_group)

            # Create Provider
            data = dict(request.data, user=user.get_api_url())
            serializer = ProviderSerializer(data=data,
                                            context={'request': request})
            serializer.is_valid(raise_exception=True)
            serializer.save()  # returns provider if we need it
            headers = self.get_success_headers(serializer.data)

            # If we got here without blowing up, send the user's activation email
            user.send_activation_email(request.site, request,
                                       data['base_activation_link'])
            return Response(serializer.data,
                            status=status.HTTP_201_CREATED,
                            headers=headers)

    @list_route(methods=['post'], permission_classes=[AllowAny])
    def claim_service(self, request, *args, **kwargs):
        try:
            with atomic():
                if request.user.groups.filter(name='Providers').exists():
                    user = request.user
                else:
                    try:
                        user = get_user_model().objects.create_user(
                            email=request.data['email'],
                            password=get_user_model(
                            ).objects.make_random_password(),
                            is_active=False)
                    except DjangoValidationError:
                        raise DjangoValidationError(
                            'User with this email exists')
                    provider_group, _ = Group.objects.get_or_create(
                        name='Providers')
                    user.groups.add(provider_group)

                # Create or update Provider
                data = dict(request.data, user=user.get_api_url())
                if hasattr(user, 'provider'):
                    serializer = ProviderSerializer(
                        user.provider, data=data, context={'request': request})
                else:
                    serializer = ProviderSerializer(
                        data=data, context={'request': request})
                try:
                    serializer.is_valid(raise_exception=True)
                except DRFValidationError:
                    raise DjangoValidationError(
                        'All fields needs to be filled.')
                serializer.save()
                headers = self.get_success_headers(serializer.data)
                service = Service.objects.get(id=request.data['service_id'])
                user.send_activation_email_to_staff(request, service,
                                                    serializer.instance)
                return Response(serializer.data,
                                status=status.HTTP_201_CREATED,
                                headers=headers)
        except DjangoValidationError as ex:
            return Response('; '.join(ex.messages),
                            status=status.HTTP_400_BAD_REQUEST)
        except Exception as ex:
            logger.error(
                'There was an error during claim',
                exc_info=True,
            )
            return Response(str(ex), status=status.HTTP_400_BAD_REQUEST)

    @detail_route(methods=['get'],
                  permission_classes=[permissions.DjangoObjectPermissions])
    def export_services(self, request, pk=None, *args, **kwargs):
        obj = self.get_queryset().filter(pk=pk).first()
        services = obj.services.all()

        book = openpyxl.Workbook()
        sheet = book.active

        provider_services = []

        for s in services:
            s = serializers_v2.ServiceExcelSerializer(s).data
            provider_services.append(s)

        headers = list(serializers_v2.ServiceExcelSerializer.FIELD_MAP.keys())
        human_headers = list(
            serializers_v2.ServiceExcelSerializer.FIELD_MAP.values())

        for col in range(0, len(human_headers)):
            sheet.cell(column=col + 1, row=1).value = human_headers[col]

        for row in range(0, len(provider_services)):
            for col in range(0, len(headers)):
                sheet.cell(column=col + 1, row=row +
                           2).value = provider_services[row][headers[col]]

        book_data = BytesIO()
        book.save(book_data)
        book_data.seek(0)

        return Response(
            {
                "data":
                base64.b64encode(book_data.read()),
                "content_type":
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            },
            content_type="application/json")

    @list_route(methods=['get'], permission_classes=[AllowAny])
    def export_services_bulk(self, request, pk=None, *args, **kwargs):
        book = openpyxl.Workbook()
        sheet = book.active

        t1 = time.time()

        human_headers = tuple(
            serializers_v2.ServiceExcelSerializer.FIELD_MAP.values())
        sheet.append(human_headers)

        services_list = Service.objects.prefetch_related(
            'types', 'confirmation_logs', 'contact_information').all()

        services_bulk = serializers_v2.ServiceExcelSerializer(services_list,
                                                              many=True).data
        for row in services_bulk:
            sheet.append(tuple(row.values()))

        book_data = BytesIO()
        book.save(book_data)
        book_data.seek(0)

        t2 = time.time()
        print('time consumed: ', t2 - t1)

        return HttpResponse(
            save_virtual_workbook(book),
            content_type=
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )

    @detail_route(methods=['GET'])
    def impersonate_provider(self, request, pk):
        request.session['selected-provider'] = pk

        return Response({})

    @detail_route(methods=['GET'])
    def stop_impersonate_provider(self, request, pk):
        if 'selected-provider' in request.session:
            del request.session['selected-provider']
            request.session.modified = True
            logger.error('**** STOP IMPERSONATING')

        return Response({})

    @detail_route(methods=['post'],
                  permission_classes=[permissions.DjangoObjectPermissions],
                  parser_classes=[parsers.MultiPartParser])
    def import_services(self, request, pk=None, *args, **kwargs):
        obj = self.get_queryset().filter(pk=pk).first()
        file = request.data['file']

        with tempfile.NamedTemporaryFile(suffix='.xlsx') as nf:
            nf.delete = True
            with open(nf.name, 'w+') as fp:
                fp.write(str(file.read()))

            book = openpyxl.load_workbook(file)
        sheet = book.get_active_sheet()

        reversed_field_map = dict([
            (d[1], d[0])
            for d in serializers_v2.ServiceExcelSerializer.FIELD_MAP.items()
        ])

        headers = [
            sheet.cell(row=1, column=c).value
            for c in range(1, sheet.max_column + 1)
        ]
        headers = [
            reversed_field_map[h] if h in reversed_field_map else h
            for h in headers
        ]

        rows = [[
            sheet.cell(row=r, column=c).value
            for c in range(1, sheet.max_column + 1)
        ] for r in range(2, sheet.max_row + 1)]

        rows_to_use = [dict(zip(headers, r)) for r in rows]
        errors = {}

        try:
            with atomic() as t:
                for a in rows_to_use:
                    for f in [
                            "name_{}".format(k) for k, v in settings.LANGUAGES
                    ]:
                        if f not in a or not a[f]:
                            a[f] = ''
                    for f in [
                            "description_{}".format(k)
                            for k, v in settings.LANGUAGES
                    ]:
                        if f not in a or not a[f]:
                            a[f] = ''
                    for f in [
                            "address_{}".format(k)
                            for k, v in settings.LANGUAGES
                    ]:
                        if f not in a or not a[f]:
                            a[f] = ''

                    serialized = serializers_v2.ServiceExcelSerializer(
                        data=a, partial=True)
                    if serialized.is_valid():
                        if 'delete' in a and a['delete']:
                            service = Service.objects.get(id=a['id'])
                            service.delete()
                            continue

                        if a['id']:
                            service = Service.objects.get(id=a['id'])
                        else:
                            region = a['region']
                            region = GeographicRegion.objects.filter(
                                id=region).first()
                            serialized.validated_data.pop('region', '')
                            service = Service(region=region,
                                              provider=obj,
                                              **serialized.validated_data)
                            service.save()

                        if 'location' in a and a['location']:
                            location = Point(*reversed([
                                float(b.strip())
                                for b in a['location'].split(',')
                            ]),
                                             srid=4326)
                        else:
                            location = service.location

                        if 'location' in serialized.validated_data:
                            del serialized.validated_data['location']

                        Service.objects.filter(id=service.id).update(
                            location=location, **serialized.validated_data)
                    else:
                        errors = serialized.errors
                        raise IntegrityError

                return Response(None, status=204)
        except IntegrityError:
            return Response(errors, status=400)
Ejemplo n.º 18
0
 def get_search_fields(self):
     if 'service-management' in self.request.get_full_path():
         return generate_translated_fields('name', False) \
             + generate_translated_fields('type__name', False) \
             + ['region__name'] \
             + ['status']
     else:
         return generate_translated_fields('additional_info', False) \
             + ['cost_of_service'] \
             + generate_translated_fields('description', False) \
             + generate_translated_fields('name', False) \
             + generate_translated_fields('type__comments', False) \
             + generate_translated_fields('type__name', False) \
             + generate_translated_fields('provider__description', False) \
             + generate_translated_fields('provider__focal_point_name', False) \
             + ['provider__focal_point_phone_number'] \
             + generate_translated_fields('provider__address', False) \
             + generate_translated_fields('provider__name', False) \
             + generate_translated_fields('provider__type__name', False) \
             + ['provider__phone_number', 'provider__website', 'provider__user__email'] \
             + generate_translated_fields('selection_criteria__text', False) \
             + ['region__slug', 'tags__name']
Ejemplo n.º 19
0
 def create(cls, **kwargs):
     for field in getattr(cls, '_translatable_fields', []):
         for translated_field in generate_translated_fields(field, False):
             cls._meta.declarations[
                 translated_field] = factory.fuzzy.FuzzyText()
     return super().create(**kwargs)
Ejemplo n.º 20
0
class ProviderTypeAdmin(admin.ModelAdmin):
    list_display = generate_translated_fields('name', False)
    list_display_links = list_display
Ejemplo n.º 21
0
class ServiceTypeAdmin(admin.ModelAdmin):
    list_display = ['number'] + generate_translated_fields('name', False) \
                   + generate_translated_fields('comments', False)
Ejemplo n.º 22
0
class ServiceAreaAdmin(VersionAdmin):
    list_display = ['geographic_region', 'pk', 'parent'
                    ] + generate_translated_fields('name', False)
Ejemplo n.º 23
0
class SelectionCriterionAdmin(admin.ModelAdmin):
    list_display = ['service'] + generate_translated_fields('text', False)
Ejemplo n.º 24
0
class ServiceAdmin(AdminImageMixin, VersionAdmin):
    class Media:
        css = {"all": ("css/service-admin.css", )}

    form = ServiceAdminForm

    actions = ['approve', 'reject']
    fieldsets = (
        (None, {
            'fields': [
                'provider',
                ('status', 'types'),
                tuple(generate_translated_fields('name', False)),
                'region',
                'cost_of_service',
                'update_of',
                'phone_number',
            ],
        }),
        (_('Address'), {
            'classes': ('collapse', ),
            'fields': generate_translated_fields('address', False)
        }),
        (_('Description and Additional Information'), {
            'classes': ('collapse', ),
            'fields':
            generate_translated_fields('description', False) +
            generate_translated_fields('additional_info', False)
        }),
        (_('Hours (all times in time zone {timezone})').format(
            timezone=settings.TIME_ZONE), {
                'classes': ('collapse', ),
                'fields': [
                    (
                        'sunday_open',
                        'sunday_close',
                    ),
                    (
                        'monday_open',
                        'monday_close',
                    ),
                    (
                        'tuesday_open',
                        'tuesday_close',
                    ),
                    (
                        'wednesday_open',
                        'wednesday_close',
                    ),
                    (
                        'thursday_open',
                        'thursday_close',
                    ),
                    (
                        'friday_open',
                        'friday_close',
                    ),
                    (
                        'saturday_open',
                        'saturday_close',
                    ),
                ]
            }),
        (_('Location'), {
            'fields': ['location'],
        }),
        (_('Image'), {
            'fields': [
                'image',
            ]
        }),
    )
    inlines = [SelectionCriterionInlineAdmin]
    list_display = generate_translated_fields('name', False) + \
                   ['provider',
                    'get_types',
                    'status',
                    'region',
                    'show_image']
    list_display_links = generate_translated_fields(
        'name', False) + ['provider', 'region']
    list_filter = ['status', 'type']
    readonly_fields = ['status']

    def approve(self, request, queryset):
        # All must be in DRAFT status
        if queryset.exclude(status=Service.STATUS_DRAFT).exists():
            self.message_user(
                request, _("Only services in draft status may be approved"),
                messages.ERROR)
            return
        any_approved = False
        for service in queryset:
            try:
                service.staff_approve(request.user)
            except ValidationError as e:
                msg = _("Unable to approve service '{name}': {error}.")
                msg = msg.format(name=service.name,
                                 error=validation_error_as_text(e))
                messages.error(request, msg)
            else:
                any_approved = True
        if any_approved:
            self.message_user(request, _("Services have been approved"))

    approve.short_description = _("Approve new or changed service")

    def reject(self, request, queryset):
        # All must be in DRAFT status
        if queryset.exclude(status=Service.STATUS_DRAFT).exists():
            self.message_user(
                request, _("Only services in draft status may be rejected"),
                messages.ERROR)
            return
        any_rejected = False
        for service in queryset:
            try:
                service.staff_reject(request.user)
            except ValidationError as e:
                msg = _("Unable to reject service '{name}': {error}.")
                msg = msg.format(name=service.name,
                                 error=validation_error_as_text(e))
                messages.error(request, msg)
            else:
                any_rejected = True
        if any_rejected:
            self.message_user(request, _("Services have been rejected"))

    reject.short_description = _("Reject new or changed service")

    def response_change(self, request, obj):
        """
        Determines the HttpResponse for the change_view stage.
        """
        if '_approve' in request.POST:
            try:
                obj.staff_approve(request.user)
            except ValidationError as e:
                msg = _("Unable to approve service '{name}': {error}.")
                msg = msg.format(name=obj.name,
                                 error=validation_error_as_text(e))
                self.message_user(request, msg, messages.ERROR)
                redirect_url = add_preserved_filters(
                    {
                        'preserved_filters':
                        self.get_preserved_filters(request),
                        'opts': self.model._meta
                    }, request.path)
                return HttpResponseRedirect(redirect_url)
            else:
                msg = _('The service was approved successfully.')
                self.message_user(request, msg, messages.SUCCESS)
        elif '_reject' in request.POST:
            try:
                obj.staff_reject(request.user)
            except ValidationError as e:
                msg = _("Unable to reject service '{name}': {error}.")
                msg = msg.format(name=obj.name,
                                 error=validation_error_as_text(e))
                self.message_user(request, msg, messages.ERROR)
                redirect_url = add_preserved_filters(
                    {
                        'preserved_filters':
                        self.get_preserved_filters(request),
                        'opts': self.model._meta
                    }, request.path)
                return HttpResponseRedirect(redirect_url)
            else:
                msg = _('The service was rejected successfully.')
                self.message_user(request, msg, messages.INFO)
        return super().response_change(request, obj)

    def show_image(self, obj):
        """Create a thumbnail of this image to show in the admin list."""
        thumbnail_url = obj.get_thumbnail_url()
        if thumbnail_url:
            return '<img src="{}" />'.format(thumbnail_url)
        return _("no image")

    def get_types(self, obj):
        return ','.join([t.name_en for t in obj.types.all()])

    show_image.allow_tags = True
    show_image.short_description = "Image"