def test_returning_columns_with_profile(self): col1 = Column.objects.create( column_name='New Column', table_name='PropertyState', organization=self.fake_org, is_extra_data=True, ) col2 = Column.objects.create( column_name='Second Column', table_name='PropertyState', organization=self.fake_org, is_extra_data=True, ) new_list_setting = ColumnListSetting.objects.create( name='example list setting') ColumnListSettingColumn.objects.create( column=col1, column_list_setting=new_list_setting, order=1, pinned=False) ColumnListSettingColumn.objects.create( column=col2, column_list_setting=new_list_setting, order=2, pinned=True) # do not set up a profile and return the columns, should be all columns ids, name_mappings, objs = ColumnListSetting.return_columns( self.fake_org, new_list_setting.id) # not the most robust tests, but they are least check for non-zero results self.assertIsInstance(ids[0], int) self.assertIsInstance(list(name_mappings.keys())[0], basestring) self.assertIsInstance(list(name_mappings.values())[0], basestring)
def test_returning_columns_no_profile(self): # do not set up a profile and return the columns, should be all columns ids, name_mappings, objs = ColumnListSetting.return_columns(self.fake_org, None) # not the most robust tests, but they are least check for non-zero results self.assertIsInstance(ids[0], int) self.assertIsInstance(list(name_mappings.keys())[0], basestring) self.assertIsInstance(list(name_mappings.values())[0], basestring)
def export(self, request): """ Download a csv of the TaxLot and Properties .. code-block:: { "ids": [1,2,3], "columns": ["tax_jurisdiction_tax_lot_id", "address_line_1", "property_view_id"] } --- parameter_strategy: replace parameters: - name: cycle description: cycle required: true paramType: query - name: inventory_type description: properties or taxlots (as defined by the inventory list page) required: true paramType: query - name: ids description: list of property/taxlot ids to export (not property/taxlot views) required: true paramType: body - name: columns description: list of columns to export required: true paramType: body - name: filename description: name of the file to create required: false paramType: body - name: profile_id description: Either an id of a list settings profile, or undefined paramType: body """ 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' }) org_id = request.query_params['organization_id'] if 'profile_id' not in request.data: profile_id = None else: if request.data['profile_id'] == 'None' or request.data[ 'profile_id'] == '': profile_id = None else: profile_id = request.data['profile_id'] # get the class to operate on and the relationships view_klass_str = request.query_params.get('inventory_type', 'properties') view_klass = INVENTORY_MODELS[view_klass_str] # Set the first column to be the ID column_name_mappings = OrderedDict([('id', 'ID')]) column_ids, add_column_name_mappings, columns_from_database = ColumnListSetting.return_columns( org_id, profile_id, view_klass_str) column_name_mappings.update(add_column_name_mappings) select_related = ['state', 'cycle'] ids = request.data.get('ids', []) filter_str = {'cycle': cycle_pk} if hasattr(view_klass, 'property'): select_related.append('property') prefetch_related = ['labels'] filter_str = {'property__organization_id': org_id} if ids: filter_str['property__id__in'] = ids # always export the labels column_name_mappings['property_labels'] = 'Property Labels' elif hasattr(view_klass, 'taxlot'): select_related.append('taxlot') prefetch_related = ['labels'] filter_str = {'taxlot__organization_id': org_id} if ids: filter_str['taxlot__id__in'] = ids # always export the labels column_name_mappings['taxlot_labels'] = 'Tax Lot Labels' model_views = view_klass.objects.select_related( *select_related).prefetch_related(*prefetch_related).filter( **filter_str).order_by('id') # get the data in a dict which includes the related data data = TaxLotProperty.get_related(model_views, column_ids, columns_from_database) # add labels for i, record in enumerate(model_views): label_string = [] if hasattr(record, 'property'): for label in list(record.labels.all().order_by('name')): label_string.append(label.name) data[i]['property_labels'] = ','.join(label_string) elif hasattr(record, 'taxlot'): for label in list(record.labels.all().order_by('name')): label_string.append(label.name) data[i]['taxlot_labels'] = ','.join(label_string) # force the data into the same order as the IDs if ids: order_dict = {obj_id: index for index, obj_id in enumerate(ids)} data.sort(key=lambda x: order_dict[x['id']] ) # x is the property/taxlot object export_type = request.data.get('export_type', 'csv') filename = request.data.get('filename', f"ExportedData.{export_type}") if export_type == "csv": return self._csv_response(filename, data, column_name_mappings) elif export_type == "geojson": return self._json_response(filename, data, column_name_mappings) elif export_type == "xlsx": return self._spreadsheet_response(filename, data, column_name_mappings)
def csv(self, request): """ Download a csv of the TaxLot and Properties .. code-block:: { "ids": [1,2,3], "columns": ["tax_jurisdiction_tax_lot_id", "address_line_1", "property_view_id"] } --- parameter_strategy: replace parameters: - name: cycle description: cycle required: true paramType: query - name: inventory_type description: properties or taxlots (as defined by the inventory list page) required: true paramType: query - name: ids description: list of property ids to export (not property views) required: true paramType: body - name: columns description: list of columns to export required: true paramType: body - name: filename description: name of the file to create required: false paramType: body - name: profile_id description: Either an id of a list settings profile, or undefined paramType: body """ 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' }) org_id = request.query_params['organization_id'] if 'profile_id' not in request.data: profile_id = None else: if request.data['profile_id'] == 'None': profile_id = None else: profile_id = request.data['profile_id'] # get the class to operate on and the relationships view_klass_str = request.query_params.get('inventory_type', 'properties') view_klass = INVENTORY_MODELS[view_klass_str] # Set the first column to be the ID column_name_mappings = OrderedDict([('id', 'ID')]) column_ids, add_column_name_mappings, columns_from_database = ColumnListSetting.return_columns( org_id, profile_id, view_klass_str) column_name_mappings.update(add_column_name_mappings) select_related = ['state', 'cycle'] ids = request.data.get('ids', []) filter_str = {'cycle': cycle_pk} if hasattr(view_klass, 'property'): select_related.append('property') filter_str = {'property__organization_id': org_id} if ids: filter_str['property__id__in'] = ids # always export the labels column_name_mappings['property_labels'] = 'Property Labels' elif hasattr(view_klass, 'taxlot'): select_related.append('taxlot') filter_str = {'taxlot__organization_id': org_id} if ids: filter_str['taxlot__id__in'] = ids # always export the labels column_name_mappings['taxlot_labels'] = 'Tax Lot Labels' model_views = view_klass.objects.select_related( *select_related).filter(**filter_str).order_by('id') filename = request.data.get('filename', "ExportedData.csv") response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="{}"'.format( filename) writer = csv.writer(response) # get the data in a dict which includes the related data data = TaxLotProperty.get_related(model_views, column_ids, columns_from_database) # force the data into the same order as the IDs if ids: order_dict = {obj_id: index for index, obj_id in enumerate(ids)} data.sort(key=lambda x: order_dict[x['id']] ) # x is the property/taxlot object # check the first item in the header and make sure that it isn't ID (it can be id, or iD). # excel doesn't like the first item to be ID in a CSV header = list(column_name_mappings.values()) if header[0] == 'ID': header[0] = 'id' writer.writerow(header) # iterate over the results to preserve column order and write row. for datum in data: row = [] for column in column_name_mappings: row_result = datum.get(column, None) # Try grabbing the value out of the related field if not found yet. if row_result is None and datum.get('related'): row_result = datum['related'][0].get(column, None) # Convert quantities (this is typically handled in the JSON Encoder, but that isn't here). if isinstance(row_result, ureg.Quantity): row_result = row_result.magnitude elif isinstance(row_result, datetime.datetime): row_result = row_result.strftime("%Y-%m-%d %H:%M:%S") elif isinstance(row_result, datetime.date): row_result = row_result.strftime("%Y-%m-%d") row.append(row_result) writer.writerow(row) return response