def retrieve(self, request, pk=None): """ Get property details --- parameters: - name: pk description: The primary key of the PropertyView required: true paramType: path - name: organization_id description: The organization_id for this user's organization required: true paramType: query """ result = self._get_property_view(pk) if result.get('status', None) != 'error': property_view = result.pop('property_view') result = {'status': 'success'} result.update(PropertyViewSerializer(property_view).data) # remove PropertyView id from result result.pop('id') result['state'] = PropertyStateSerializer(property_view.state).data result['taxlots'] = self._get_taxlots(property_view.pk) result['history'], master = self.get_history(property_view) result = update_result_with_master(result, master) return JsonResponse(result, encoder=PintJSONEncoder, status=status.HTTP_200_OK) else: return JsonResponse(result)
def retrieve(self, request, pk=None): """ Get property details --- parameters: - name: cycle_id description: The cycle id for filtering the property 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_property_view(pk, cycle_pk) if result.get('status', None) != 'error': property_view = result.pop('property_view') result.update(PropertyViewSerializer(property_view).data) # remove PropertyView id from result result.pop('id') result['state'] = PropertyStateSerializer(property_view.state).data result['taxlots'] = self._get_taxlots(property_view.pk) result['history'], master = self.get_history(property_view) result = update_result_with_master(result, master) status_code = status.HTTP_200_OK else: status_code = status.HTTP_404_NOT_FOUND return Response(result, status=status_code)
def get_history(self, property_view): """Return history in reverse order""" # access the history from the property state history, master = property_view.state.history() # convert the history and master states to StateSerializers master['state'] = PropertyStateSerializer(master['state_data']).data del master['state_data'] del master['state_id'] for h in history: h['state'] = PropertyStateSerializer(h['state_data']).data del h['state_data'] del h['state_id'] return history, master
class AnalysisPropertyViewSerializer(serializers.ModelSerializer): output_files = AnalysisOutputFileSerializer( source='analysisoutputfile_set', many=True) property_state = PropertyStateSerializer() class Meta: model = AnalysisPropertyView fields = '__all__'
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': PropertyStateSerializer(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 }
def retrieve(self, request, pk=None): """ Get property details --- parameters: - name: pk description: The primary key of the PropertyView required: true paramType: path - name: organization_id description: The organization_id for this user's organization required: true paramType: query """ result = self._get_property_view(pk) if result.get('status', None) != 'error': property_view = result.pop('property_view') result = {'status': 'success'} result.update(PropertyViewSerializer(property_view).data) # remove PropertyView id from result result.pop('id') # Grab extra_data columns to be shown in the result organization_id = request.query_params['organization_id'] all_extra_data_columns = Column.objects.filter( organization_id=organization_id, is_extra_data=True, table_name='PropertyState').values_list('column_name', flat=True) result['state'] = PropertyStateSerializer( property_view.state, all_extra_data_columns=all_extra_data_columns).data result['taxlots'] = self._get_taxlots(property_view.pk) result['history'], master = self.get_history(property_view) result = update_result_with_master(result, master) return JsonResponse(result, encoder=PintJSONEncoder, status=status.HTTP_200_OK) else: return JsonResponse(result)
def update(self, request, pk=None): """ Update a property --- parameters: - name: cycle_id description: The cycle id for filtering the property view 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' }) data = request.data result = self._get_property_view(pk, cycle_pk) if result.get('status', None) != 'error': property_view = result.pop('property_view') property_state_data = PropertyStateSerializer( property_view.state).data new_property_state_data = data['state'] changed = True for key, val in new_property_state_data.iteritems(): if val == '': new_property_state_data[key] = None changed_fields = get_changed_fields(property_state_data, new_property_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 = PropertyAuditLog.objects.select_related().filter( state=property_view.state).order_by('-id').first() if 'extra_data' in new_property_state_data.keys(): property_state_data['extra_data'].update( new_property_state_data.pop('extra_data')) property_state_data.update(new_property_state_data) if log.name == 'Import Creation': # Add new state property_state_data.pop('id') new_property_state_serializer = PropertyStateSerializer( data=property_state_data) if new_property_state_serializer.is_valid(): new_state = new_property_state_serializer.save() property_view.state = new_state property_view.save() PropertyAuditLog.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_property_state_serializer.validated_data }) # Removing organization key AND import_file key because they're not JSON-serializable # TODO find better solution if 'organization' in result['state']: result['state'].pop('organization') if 'import_file' in result['state']: 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 = property_view.state for key, value in new_property_state_data.iteritems(): setattr(state, key, value) state.save() result.update( {'state': PropertyStateSerializer(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)
def get_history(self, property_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': PropertyStateSerializer(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 = PropertyAuditLog.objects.select_related( 'state', 'parent1', 'parent2').filter( state_id=property_view.state_id).order_by('-id').first() master = { 'state': PropertyStateSerializer(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
def update(self, request, pk=None): """ Update a property and run the updated record through a match and merge round within it's current Cycle. - looks up the property view - casts it as a PropertyState - builds a hash with all the same keys as the original property state - checks if any fields have changed - if nothing has changed, return 422 - Really? Not sure how I feel about that one, it *is* processable - get the property audit log for this property state - if the new property state has extra_data, the original extra_data is update'd - and then whoa stuff about the audit log? - I'm going to assume 'Import Creation' is the key I'm looking for - create a serializer for the new property state - if it's valid, save this new serialized data to the db - assign it to the original property view and save the property view - create a new property audit log for this change - return a 200 if created --- parameters: - name: organization_id description: The organization_id for this user's organization required: true paramType: query """ data = request.data result = self._get_property_view(pk) if result.get('status', None) != 'error': property_view = result.pop('property_view') property_state_data = PropertyStateSerializer( property_view.state).data # get the property state information from the request new_property_state_data = data['state'] # set empty strings to None for key, val in new_property_state_data.items(): if val == '': new_property_state_data[key] = None changed_fields = get_changed_fields(property_state_data, new_property_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 = PropertyAuditLog.objects.select_related().filter( state=property_view.state).order_by('-id').first() if 'extra_data' in new_property_state_data: property_state_data['extra_data'].update( new_property_state_data.pop('extra_data')) property_state_data.update(new_property_state_data) if log.name == 'Import Creation': # Add new state by removing the existing ID. property_state_data.pop('id') new_property_state_serializer = PropertyStateSerializer( data=property_state_data) if new_property_state_serializer.is_valid(): # create the new property state, and perform an initial save / moving relationships new_state = new_property_state_serializer.save() # Since we are creating a new relationship when we are manually editing the Properties, then # we need to move the relationships over to the new manually edited record. new_state = self._move_relationships( property_view.state, new_state) new_state.save() # then assign this state to the property view and save the whole view property_view.state = new_state property_view.save() PropertyAuditLog.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_property_state_serializer.data}) # save the property view so that the datetime gets updated on the property. property_view.save() count, view_id = match_merge_in_cycle( property_view.id, 'PropertyState') if view_id is not None: result.update({ 'view_id': view_id, 'match_merged_count': count, }) return JsonResponse(result, encoder=PintJSONEncoder, status=status.HTTP_200_OK) else: result.update({ 'status': 'error', 'message': 'Invalid update data with errors: {}'.format( new_property_state_serializer.errors) }) return JsonResponse( result, encoder=PintJSONEncoder, 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_property_state_serializer = PropertyStateSerializer( property_view.state, data=property_state_data) if updated_property_state_serializer.is_valid(): # create the new property state, and perform an initial save / moving # relationships updated_property_state_serializer.save() result.update( {'state': updated_property_state_serializer.data}) # save the property view so that the datetime gets updated on the property. property_view.save() count, view_id = match_merge_in_cycle( property_view.id, 'PropertyState') if view_id is not None: result.update({ 'view_id': view_id, 'match_merged_count': count, }) return JsonResponse(result, encoder=PintJSONEncoder, status=status.HTTP_200_OK) else: result.update({ 'status': 'error', 'message': 'Invalid update data with errors: {}'.format( updated_property_state_serializer.errors) }) return JsonResponse( result, encoder=PintJSONEncoder, status=status.HTTP_422_UNPROCESSABLE_ENTITY) else: result = { 'status': 'error', 'message': 'Unrecognized audit log name: ' + log.name } return JsonResponse( result, status=status.HTTP_422_UNPROCESSABLE_ENTITY) else: return JsonResponse(result, status=status.HTTP_404_NOT_FOUND)