Exemple #1
0
    def retrieve(self, request, pk):
        """
        Get taxlot details
        ---
        parameters:
            - name: pk
              description: The primary key of the TaxLotView
              required: true
              paramType: path
            - name: organization_id
              description: The organization_id for this user's organization
              required: true
              paramType: query
        """
        result = self._get_taxlot_view(pk)
        if result.get('status', None) != 'error':
            taxlot_view = result.pop('taxlot_view')
            result.update(TaxLotViewSerializer(taxlot_view).data)
            # remove TaxLotView id from result
            result.pop('id')

            result['state'] = TaxLotStateSerializer(taxlot_view.state).data
            result['properties'] = self._get_properties(taxlot_view.pk)
            result['history'], master = self.get_history(taxlot_view)
            result = update_result_with_master(result, master)
            return JsonResponse(result, status=status.HTTP_200_OK)
        else:
            return JsonResponse(result, status=status.HTTP_404_NOT_FOUND)
Exemple #2
0
    def links(self, request, pk=None):
        """
        Get taxlot details for each linked taxlot across org cycles
        """
        organization_id = request.query_params.get('organization_id', None)
        base_view = TaxLotView.objects.select_related('cycle').filter(
            pk=pk,
            cycle__organization_id=organization_id
        )

        if base_view.exists():
            result = {'data': []}

            linked_views = TaxLotView.objects.select_related('cycle').filter(
                taxlot_id=base_view.get().taxlot_id,
                cycle__organization_id=organization_id
            ).order_by('-cycle__start')
            for linked_view in linked_views:
                state_data = TaxLotStateSerializer(linked_view.state).data

                state_data['cycle_id'] = linked_view.cycle.id
                state_data['view_id'] = linked_view.id
                result['data'].append(state_data)

            return JsonResponse(result, status=status.HTTP_200_OK)
        else:
            result = {
                'status': 'error',
                'message': 'property view with id {} does not exist in given organization'.format(pk)
            }
            return JsonResponse(result)
Exemple #3
0
    def get_history(self, taxlot_view):
        """Return history in reverse order"""

        # access the history from the property state
        history, master = taxlot_view.state.history()

        # convert the history and master states to StateSerializers
        master['state'] = TaxLotStateSerializer(master['state_data']).data
        del master['state_data']
        del master['state_id']

        for h in history:
            h['state'] = TaxLotStateSerializer(h['state_data']).data
            del h['state_data']
            del h['state_id']

        return history, master
Exemple #4
0
 def record_dict(log):
     filename = None if not log.import_filename else path.basename(
         log.import_filename)
     if filename:
         # Attempt to remove NamedTemporaryFile suffix
         name, ext = path.splitext(filename)
         pattern = re.compile('(.*?)(_[a-zA-Z0-9]{7})$')
         match = pattern.match(name)
         if match:
             filename = match.groups()[0] + ext
     return {
         'state': TaxLotStateSerializer(log.state).data,
         'date_edited': convert_to_js_timestamp(log.created),
         'source': log.get_record_type_display(),
         'filename': filename,
         # 'changed_fields': json.loads(log.description) if log.record_type == AUDIT_USER_EDIT else None
     }
Exemple #5
0
    def retrieve(self, request, pk):
        """
        Get taxlot details
        """
        result = self._get_taxlot_view(pk)
        if result.get('status', None) != 'error':
            taxlot_view = result.pop('taxlot_view')
            result.update(TaxLotViewSerializer(taxlot_view).data)
            # remove TaxLotView id from result
            result.pop('id')

            result['state'] = TaxLotStateSerializer(taxlot_view.state).data
            result['properties'] = self._get_properties(taxlot_view.pk)
            result['history'], master = self.get_history(taxlot_view)
            result = update_result_with_master(result, master)
            return JsonResponse(result, status=status.HTTP_200_OK)
        else:
            return JsonResponse(result, status=status.HTTP_404_NOT_FOUND)
Exemple #6
0
 def retrieve(self, request, pk):
     """
     Get property details
     ---
     parameters:
         - name: cycle_id
           description: The cycle id for filtering the taxlot view
           required: true
           paramType: query
         - name: organization_id
           description: The organization_id for this user's organization
           required: true
           paramType: query
     """
     cycle_pk = request.query_params.get('cycle_id', None)
     if not cycle_pk:
         return JsonResponse({
             'status':
             'error',
             'message':
             'Must pass in cycle_id as query parameter'
         })
     result = self._get_taxlot_view(pk, cycle_pk)
     if result.get('status', None) != 'error':
         taxlot_view = result.pop('taxlot_view')
         result.update(TaxLotViewSerializer(taxlot_view).data)
         # remove TaxLotView id from result
         result.pop('id')
         result['state'] = TaxLotStateSerializer(taxlot_view.state).data
         result['properties'] = self._get_properties(taxlot_view.pk)
         result['history'], master = self.get_history(taxlot_view)
         result = update_result_with_master(result, master)
         status_code = status.HTTP_200_OK
     else:
         status_code = status.HTTP_404_NOT_FOUND
     return JsonResponse(result, status=status_code)
Exemple #7
0
    def update(self, request, pk):
        """
        Update a taxlot
        ---
        parameters:
            - name: cycle_id
              description: The cycle id for filtering the taxlot view
              required: true
              paramType: query
        """
        data = request.data
        cycle_pk = request.query_params.get('cycle_id', None)
        if not cycle_pk:
            return JsonResponse({
                'status':
                'error',
                'message':
                'Must pass in cycle_id as query parameter'
            })
        result = self._get_taxlot_view(pk, cycle_pk)
        if result.get('status', None) != 'error':
            taxlot_view = result.pop('taxlot_view')
            taxlot_state_data = TaxLotStateSerializer(taxlot_view.state).data
            new_taxlot_state_data = data['state']

            changed = True
            for key, val in new_taxlot_state_data.iteritems():
                if val == '':
                    new_taxlot_state_data[key] = None
            changed_fields = get_changed_fields(taxlot_state_data,
                                                new_taxlot_state_data)
            if not changed_fields:
                changed = False
            if not changed:
                result.update({
                    'status': 'error',
                    'message': 'Nothing to update'
                })
                status_code = 422  # status.HTTP_422_UNPROCESSABLE_ENTITY
            else:
                log = TaxLotAuditLog.objects.select_related().filter(
                    state=taxlot_view.state).order_by('-id').first()

                if 'extra_data' in new_taxlot_state_data.keys():
                    taxlot_state_data['extra_data'].update(
                        new_taxlot_state_data.pop('extra_data'))
                taxlot_state_data.update(new_taxlot_state_data)

                if log.name == 'Import Creation':
                    # Add new state
                    taxlot_state_data.pop('id')
                    new_taxlot_state_serializer = TaxLotStateSerializer(
                        data=taxlot_state_data)
                    if new_taxlot_state_serializer.is_valid():
                        new_state = new_taxlot_state_serializer.save()
                        taxlot_view.state = new_state
                        taxlot_view.save()

                        TaxLotAuditLog.objects.create(
                            organization=log.organization,
                            parent1=log,
                            parent2=None,
                            parent_state1=log.state,
                            parent_state2=None,
                            state=new_state,
                            name='Manual Edit',
                            description=None,
                            import_filename=log.import_filename,
                            record_type=AUDIT_USER_EDIT)

                        result.update({
                            'state':
                            new_taxlot_state_serializer.validated_data
                        })
                        # Removing organization key AND import_file key because they're not JSON-serializable
                        # TODO find better solution
                        result['state'].pop('organization')
                        result['state'].pop('import_file')
                        status_code = status.HTTP_201_CREATED
                    else:
                        result.update({
                            'status': 'error',
                            'message': 'Invalid Data'
                        })
                        status_code = 422  # status.HTTP_422_UNPROCESSABLE_ENTITY
                elif log.name in [
                        'Manual Edit', 'Manual Match', 'System Match',
                        'Merge current state in migration'
                ]:
                    # Override previous edit state or merge state
                    state = taxlot_view.state
                    for key, value in new_taxlot_state_data.iteritems():
                        setattr(state, key, value)
                    state.save()

                    result.update({'state': TaxLotStateSerializer(state).data})
                    # Removing organization key AND import_file key because they're not JSON-serializable
                    # TODO find better solution
                    result['state'].pop('organization')
                    result['state'].pop('import_file')

                    status_code = status.HTTP_201_CREATED
                else:
                    result = {
                        'status': 'error',
                        'message': 'Unrecognized audit log name: ' + log.name
                    }
                    status_code = 422
                    return JsonResponse(result, status=status_code)

        else:
            status_code = status.HTTP_404_NOT_FOUND
        return JsonResponse(result, status=status_code)
Exemple #8
0
    def get_history(self, taxlot_view):
        """Return history in reverse order."""
        history = []

        def record_dict(log):
            filename = None if not log.import_filename else path.basename(
                log.import_filename)
            if filename:
                # Attempt to remove NamedTemporaryFile suffix
                name, ext = path.splitext(filename)
                pattern = re.compile('(.*?)(_[a-zA-Z0-9]{7})$')
                match = pattern.match(name)
                if match:
                    filename = match.groups()[0] + ext
            return {
                'state': TaxLotStateSerializer(log.state).data,
                'date_edited': convert_to_js_timestamp(log.created),
                'source': log.get_record_type_display(),
                'filename': filename,
                # 'changed_fields': json.loads(log.description) if log.record_type == AUDIT_USER_EDIT else None
            }

        log = TaxLotAuditLog.objects.select_related(
            'state', 'parent1', 'parent2').filter(
                state_id=taxlot_view.state_id).order_by('-id').first()
        master = {
            'state': TaxLotStateSerializer(log.state).data,
            'date_edited': convert_to_js_timestamp(log.created),
        }

        # Traverse parents and add to history
        if log.name in [
                'Manual Match', 'System Match',
                'Merge current state in migration'
        ]:
            done_searching = False
            while not done_searching:
                if (log.parent1_id is None and
                        log.parent2_id is None) or log.name == 'Manual Edit':
                    done_searching = True
                elif log.name == 'Merge current state in migration':
                    record = record_dict(log.parent1)
                    history.append(record)
                    if log.parent1.name == 'Import Creation':
                        done_searching = True
                    else:
                        tree = log.parent1
                        log = tree
                else:
                    tree = None
                    if log.parent2:
                        if log.parent2.name in [
                                'Import Creation', 'Manual Edit'
                        ]:
                            record = record_dict(log.parent2)
                            history.append(record)
                        elif log.parent2.name == 'System Match' and log.parent2.parent1.name == 'Import Creation' and \
                                log.parent2.parent2.name == 'Import Creation':
                            # Handle case where an import file matches within itself, and proceeds to match with
                            # existing records
                            record = record_dict(log.parent2.parent2)
                            history.append(record)
                            record = record_dict(log.parent2.parent1)
                            history.append(record)
                        else:
                            tree = log.parent2
                    if log.parent1.name in ['Import Creation', 'Manual Edit']:
                        record = record_dict(log.parent1)
                        history.append(record)
                    else:
                        tree = log.parent1

                    if not tree:
                        done_searching = True
                    else:
                        log = tree
        elif log.name == 'Manual Edit':
            record = record_dict(log.parent1)
            history.append(record)
        elif log.name == 'Import Creation':
            record = record_dict(log)
            history.append(record)

        return history, master
Exemple #9
0
    def update(self, request, pk):
        """
        Update a taxlot and run the updated record through a match and merge
        round within it's current Cycle.
        ---
        parameters:
            - name: organization_id
              description: The organization_id for this user's organization
              required: true
              paramType: query
        """
        data = request.data

        result = self._get_taxlot_view(pk)
        if result.get('status', 'error') != 'error':
            taxlot_view = result.pop('taxlot_view')
            taxlot_state_data = TaxLotStateSerializer(taxlot_view.state).data

            # get the taxlot state information from the request
            new_taxlot_state_data = data['state']

            # set empty strings to None
            for key, val in new_taxlot_state_data.items():
                if val == '':
                    new_taxlot_state_data[key] = None

            changed_fields, previous_data = get_changed_fields(
                taxlot_state_data, new_taxlot_state_data)
            if not changed_fields:
                result.update({
                    'status': 'success',
                    'message': 'Records are identical'
                })
                return JsonResponse(result, status=status.HTTP_204_NO_CONTENT)
            else:
                # Not sure why we are going through the pain of logging this all right now... need to
                # reevaluate this.
                log = TaxLotAuditLog.objects.select_related().filter(
                    state=taxlot_view.state).order_by('-id').first()

                # if checks above pass, create an exact copy of the current state for historical purposes
                if log.name == 'Import Creation':
                    # Add new state by removing the existing ID.
                    taxlot_state_data.pop('id')
                    # Remove the import_file_id for the first edit of a new record
                    # If the import file has been deleted and this value remains the serializer won't be valid
                    taxlot_state_data.pop('import_file')
                    new_taxlot_state_serializer = TaxLotStateSerializer(
                        data=taxlot_state_data)
                    if new_taxlot_state_serializer.is_valid():
                        # create the new property state, and perform an initial save / moving relationships
                        new_state = new_taxlot_state_serializer.save()

                        # then assign this state to the property view and save the whole view
                        taxlot_view.state = new_state
                        taxlot_view.save()

                        TaxLotAuditLog.objects.create(
                            organization=log.organization,
                            parent1=log,
                            parent2=None,
                            parent_state1=log.state,
                            parent_state2=None,
                            state=new_state,
                            name='Manual Edit',
                            description=None,
                            import_filename=log.import_filename,
                            record_type=AUDIT_USER_EDIT)

                        result.update(
                            {'state': new_taxlot_state_serializer.data})

                        # save the property view so that the datetime gets updated on the property.
                        taxlot_view.save()
                    else:
                        result.update({
                            'status':
                            'error',
                            'message':
                            'Invalid update data with errors: {}'.format(
                                new_taxlot_state_serializer.errors)
                        })
                        return JsonResponse(
                            result,
                            status=status.HTTP_422_UNPROCESSABLE_ENTITY)

                # redo assignment of this variable in case this was an initial edit
                taxlot_state_data = TaxLotStateSerializer(
                    taxlot_view.state).data

                if 'extra_data' in new_taxlot_state_data:
                    taxlot_state_data['extra_data'].update(
                        new_taxlot_state_data['extra_data'])

                taxlot_state_data.update({
                    k: v
                    for k, v in new_taxlot_state_data.items()
                    if k != 'extra_data'
                })

                log = TaxLotAuditLog.objects.select_related().filter(
                    state=taxlot_view.state).order_by('-id').first()

                if log.name in [
                        'Manual Edit', 'Manual Match', 'System Match',
                        'Merge current state in migration'
                ]:
                    # Convert this to using the serializer to save the data. This will override the
                    # previous values in the state object.

                    # Note: We should be able to use partial update here and pass in the changed
                    # fields instead of the entire state_data.
                    updated_taxlot_state_serializer = TaxLotStateSerializer(
                        taxlot_view.state, data=taxlot_state_data)
                    if updated_taxlot_state_serializer.is_valid():
                        # create the new property state, and perform an initial save / moving
                        # relationships
                        updated_taxlot_state_serializer.save()

                        result.update(
                            {'state': updated_taxlot_state_serializer.data})

                        # save the property view so that the datetime gets updated on the property.
                        taxlot_view.save()

                        Note.create_from_edit(request.user.id, taxlot_view,
                                              new_taxlot_state_data,
                                              previous_data)

                        merge_count, link_count, view_id = match_merge_link(
                            taxlot_view.id, 'TaxLotState')

                        result.update({
                            'view_id': view_id,
                            'match_merged_count': merge_count,
                            'match_link_count': link_count,
                        })

                        return JsonResponse(result, status=status.HTTP_200_OK)
                    else:
                        result.update({
                            'status':
                            'error',
                            'message':
                            'Invalid update data with errors: {}'.format(
                                updated_taxlot_state_serializer.errors)
                        })
                        return JsonResponse(
                            result,
                            status=status.HTTP_422_UNPROCESSABLE_ENTITY)
                else:
                    result = {
                        'status': 'error',
                        'message': 'Unrecognized audit log name: ' + log.name
                    }
                    return JsonResponse(result,
                                        status=status.HTTP_204_NO_CONTENT)
        else:
            return JsonResponse(result, status=status.HTTP_404_NOT_FOUND)
Exemple #10
0
    def update(self, request, pk):
        """
        Update a taxlot
        ---
        parameters:
            - name: organization_id
              description: The organization_id for this user's organization
              required: true
              paramType: query
        """
        data = request.data

        result = self._get_taxlot_view(pk)
        if result.get('status', 'error') != 'error':
            taxlot_view = result.pop('taxlot_view')
            taxlot_state_data = TaxLotStateSerializer(taxlot_view.state).data

            # get the taxlot state information from the request
            new_taxlot_state_data = data['state']

            # set empty strings to None
            for key, val in new_taxlot_state_data.iteritems():
                if val == '':
                    new_taxlot_state_data[key] = None

            changed_fields = get_changed_fields(taxlot_state_data,
                                                new_taxlot_state_data)
            if not changed_fields:
                result.update({
                    'status': 'success',
                    'message': 'Records are identical'
                })
                return JsonResponse(result, status=status.HTTP_204_NO_CONTENT)
            else:
                # Not sure why we are going through the pain of logging this all right now... need to
                # reevaluate this.
                log = TaxLotAuditLog.objects.select_related().filter(
                    state=taxlot_view.state).order_by('-id').first()

                if 'extra_data' in new_taxlot_state_data.keys():
                    taxlot_state_data['extra_data'].update(
                        new_taxlot_state_data.pop('extra_data'))
                taxlot_state_data.update(new_taxlot_state_data)

                if log.name == 'Import Creation':
                    # Add new state by removing the existing ID.
                    taxlot_state_data.pop('id')
                    new_taxlot_state_serializer = TaxLotStateSerializer(
                        data=taxlot_state_data)
                    if new_taxlot_state_serializer.is_valid():
                        # create the new property state, and perform an initial save / moving relationships
                        new_state = new_taxlot_state_serializer.save()

                        # then assign this state to the property view and save the whole view
                        taxlot_view.state = new_state
                        taxlot_view.save()

                        TaxLotAuditLog.objects.create(
                            organization=log.organization,
                            parent1=log,
                            parent2=None,
                            parent_state1=log.state,
                            parent_state2=None,
                            state=new_state,
                            name='Manual Edit',
                            description=None,
                            import_filename=log.import_filename,
                            record_type=AUDIT_USER_EDIT)

                        result.update(
                            {'state': new_taxlot_state_serializer.data})

                        return JsonResponse(result, status=status.HTTP_200_OK)
                    else:
                        result.update({
                            'status':
                            'error',
                            'message':
                            'Invalid update data with errors: {}'.format(
                                new_taxlot_state_serializer.errors)
                        })
                        return JsonResponse(
                            result,
                            status=status.HTTP_422_UNPROCESSABLE_ENTITY)
                elif log.name in [
                        'Manual Edit', 'Manual Match', 'System Match',
                        'Merge current state in migration'
                ]:
                    # Convert this to using the serializer to save the data. This will override the previous values
                    # in the state object.

                    # Note: We should be able to use partial update here and pass in the changed fields instead of the
                    # entire state_data.
                    updated_taxlot_state_serializer = TaxLotStateSerializer(
                        taxlot_view.state, data=taxlot_state_data)
                    if updated_taxlot_state_serializer.is_valid():
                        # create the new property state, and perform an initial save / moving relationships
                        updated_taxlot_state_serializer.save()

                        result.update(
                            {'state': updated_taxlot_state_serializer.data})

                        return JsonResponse(result, status=status.HTTP_200_OK)
                    else:
                        result.update({
                            'status':
                            'error',
                            'message':
                            'Invalid update data with errors: {}'.format(
                                updated_taxlot_state_serializer.errors)
                        })
                        return JsonResponse(
                            result,
                            status=status.HTTP_422_UNPROCESSABLE_ENTITY)
                else:
                    result = {
                        'status': 'error',
                        'message': 'Unrecognized audit log name: ' + log.name
                    }
                    return JsonResponse(result,
                                        status=status.HTTP_204_NO_CONTENT)

            # save the tax lot view, even if it hasn't changed so that the datetime gets updated on the taxlot.
            # Uhm, does this ever get called? There are a bunch of returns in the code above.
            taxlot_view.save()
        else:
            return JsonResponse(result, status=status.HTTP_404_NOT_FOUND)

        return JsonResponse(result, status=status.HTTP_404_NOT_FOUND)