def download_report(request, type_, datetime_start, datetime_end, **kwargs): ticket_report = get_ticket_report(request, type_, datetime_start, datetime_end, **kwargs)[0] business_report = get_business_report(request, type_, datetime_start, datetime_end, **kwargs)[0] virtual_resource_report = get_virtual_resource_report(request, type_, datetime_start, datetime_end, **kwargs)[0] physical_resource_report = get_physical_resource_report(request, type_, datetime_start, datetime_end, **kwargs)[0] wb = Workbook() ws = wb.active left_align = Alignment(horizontal="left", vertical="center") center_align = Alignment(horizontal="center", vertical="center") # 自动换行 wrap_text_center_align = Alignment(wrapText=True, horizontal="left", vertical="center") thin_style = borders.Side(border_style=borders.BORDER_THIN, color=colors.BLACK) medium_style = borders.Side(border_style=borders.BORDER_MEDIUM, color=colors.BLACK) thin_border = borders.Border(top=thin_style, bottom=thin_style, left=thin_style, right=thin_style) def set_cell_content(column, row, content, alignment=left_align, border=borders.DEFAULT_BORDER): place = "%s%s" % (column, row) ws[place] = content ws[place].font = Font(name=u"宋体", size=12) ws[place].alignment = alignment ws[place].border = border def set_row_content(row, contents, alignment=left_align, border=borders.DEFAULT_BORDER): for c_index, content in enumerate(contents, 1): set_cell_content(get_column_letter(c_index), row, content, alignment, border) ws.merge_cells("A1:D1") if type_ == "day": title = datetime_start.strftime(u"%Y-%m-%d 运维日报") elif type_ == "week": title = u"%s到%s 运维周报" % (datetime_start.strftime("%Y-%m-%d"), datetime_end.strftime("%Y-%m-%d")) else: title = datetime_start.strftime(u"%Y-%m 运维月报") set_cell_content("A", 1, title) ws["A1"].font = Font(name=u"宋体", size=16) ws["A1"].alignment = center_align ws.merge_cells("A2:D2") type_pretty = { "day": u"日", "week": u"周", "month": u"月", }[type_] summary = u"总结:\n" summary += u"本%s共%s个工单 ," % (type_pretty, ticket_report["ticket_num"]) if ticket_report["finish_rate_low_tickets"]: summary += u"本%s %s 完成能力弱,请注意处理积压工单。\n" % ( type_pretty, ",".join(ticket_report["finish_rate_low_tickets"]) ) else: summary += u"无工单完成能力弱。" hlc_dict = physical_resource_report["high_load_cabinet"] if hlc_dict["cpu"] or hlc_dict["memory"] or hlc_dict["disk"]: summary += u"本周" if hlc_dict["cpu"]: summary += u"%s CPU使用率为高负载," % ",".join(hlc_dict["cpu"]) if hlc_dict["memory"]: summary += u"%s 内存使用率为高负载," % ",".join(hlc_dict["memory"]) if hlc_dict["disk"]: summary += u"%s 硬盘使用率为高负载," % ",".join(hlc_dict["disk"]) summary += u"请注意资源使用情况。\n" if business_report["high_risk_systems"]: summary += u"本%s %s 系统安全风险高,请及时防范。" % (type_, ",".join(business_report["high_risk_systems"])) set_cell_content("A", 2, summary, alignment=wrap_text_center_align) # 工单情况 ws.merge_cells("A4:D4") set_cell_content("A", 4, u"工单情况", alignment=center_align) set_cell_content("A", 5, u"工单数量统计") set_row_content(6, [u"工单类型", u"工单数量", u"占比"], border=thin_border) current_row = 7 tickets = ticket_report["ticket"] ticket_sum = sum(tickets["totals"]) for index, ticket_name in enumerate(tickets["names"]): ticket_num = tickets["totals"][index] set_row_content( current_row, [ticket_name, ticket_num, result_precess(ticket_num * 100.0 / ticket_sum)], border=thin_border ) current_row += 1 current_row += 1 set_cell_content("A", current_row, u"工单处理能力") current_row += 1 set_row_content(current_row, [u"工单类型", u"完成工单数量", u"工单总量", u"完成率"], border=thin_border) current_row += 1 for index, ticket_name in enumerate(tickets["names"]): ticket_num = tickets["totals"][index] finish_num = tickets["finish_nums"][index] finish_rate = tickets["finish_rates"][index] set_row_content(current_row, [ticket_name, finish_num, ticket_num, finish_rate], border=thin_border) current_row += 1 # 资源情况 current_row += 1 ws.merge_cells("A%s:D%s" % (current_row, current_row)) set_cell_content("A", current_row, u"资源情况", alignment=center_align) current_row += 1 set_cell_content("A", current_row, u'机柜资源使用率') current_row += 1 set_row_content(current_row, [u'机柜名称', u'CPU使用率', u'内存使用率', u'硬盘使用率'], border=thin_border) current_row += 1 cur_dict = physical_resource_report["cabinet_use_rate"] for index, cabinet_name in enumerate(cur_dict["names"]): cpu_use_rate = cur_dict["cpu_use_rates"][index] memory_use_rate = cur_dict["memory_use_rates"][index] disk_use_rate = cur_dict["disk_use_rates"][index] set_row_content(current_row, [cabinet_name, cpu_use_rate, memory_use_rate, disk_use_rate], border=thin_border) current_row += 1 current_row += 1 set_cell_content("A", current_row, u'虚拟资源变化量') current_row += 1 set_row_content(current_row, [u'虚拟资源类型', u'原总量', u'变化量', u'变化率'], border=thin_border) current_row += 1 change_dict = virtual_resource_report["change"] for index, name in enumerate(change_dict["names"]): raw_num = change_dict["raw_nums"][index] num_change = change_dict["num_changes"][index] rate_change = change_dict["rate_changes"][index] set_row_content(current_row, [name, raw_num, num_change, rate_change], border=thin_border) current_row += 1 current_row += 1 set_cell_content("A", current_row, u'物理机负载趋势') current_row += 1 set_row_content(current_row, [u'时间', u'CPU使用率', u'硬盘使用率', u'内存使用率'], border=thin_border) current_row += 1 pm_load_trend = physical_resource_report["load_trend"] for index, time_ in enumerate(pm_load_trend["times"]): if type_ == "day": time_str = datetime.datetime.fromtimestamp(time_).strftime("%Y-%m-%d:%H") else: time_str = datetime.datetime.fromtimestamp(time_).strftime("%Y-%m-%d") cpu_use_rate = pm_load_trend["cpu_use_rates"][index] memory_use_rate = pm_load_trend["memory_use_rates"][index] disk_use_rate = pm_load_trend["disk_use_rates"][index] set_row_content(current_row, [time_str, cpu_use_rate, memory_use_rate, disk_use_rate], border=thin_border) current_row += 1 current_row += 1 set_cell_content("A", current_row, u'虚拟机负载趋势') current_row += 1 set_row_content(current_row, [u'时间', u'CPU使用率', u'硬盘使用率', u'内存使用率'], border=thin_border) current_row += 1 vm_load_trend = virtual_resource_report["load_trend"] for index, time_ in enumerate(vm_load_trend["times"]): if type_ == "day": time_str = datetime.datetime.fromtimestamp(time_).strftime("%Y-%m-%d:%H") else: time_str = datetime.datetime.fromtimestamp(time_).strftime("%Y-%m-%d") cpu_use_rate = vm_load_trend["cpu_use_rates"][index] memory_use_rate = vm_load_trend["memory_use_rates"][index] disk_use_rate = vm_load_trend["disk_use_rates"][index] set_row_content(current_row, [time_str, cpu_use_rate, memory_use_rate, disk_use_rate], border=thin_border) current_row += 1 # 业务监控情况 current_row += 1 ws.merge_cells("A%s:D%s" % (current_row, current_row)) set_cell_content("A", current_row, u"业务监控情况", alignment=center_align) current_row += 1 systems = business_report["systems"] for system in systems: ws.merge_cells("A%s:D%s" % (current_row, current_row)) set_cell_content("A", current_row, system["name"], alignment=center_align) current_row += 1 set_cell_content('A', current_row, u'虚拟机使用') current_row += 1 set_row_content(current_row, [u'运行中虚拟机数量', u'虚拟机总量', u'虚拟机使用率'], border=thin_border) current_row += 1 running_vm_num = system['running_vm_num'] vm_num = system['vm_num'] set_row_content( current_row, [running_vm_num, vm_num, result_precess(running_vm_num * 100.0 / vm_num) if vm_num else "-"], border=thin_border ) current_row += 2 set_cell_content('A', current_row, u'安全状况') current_row += 1 set_row_content(current_row, [u'安全风险类型', u'风险数量'], border=thin_border) current_row += 1 risks = [ ('os_leak', u'系统漏洞'), ('site_leak', u'病毒检测'), ('weak_order', u'弱口令'), ('horse_file', u'木马文件'), ] for risk, risk_name in risks: set_row_content(current_row, [risk_name, system[risk]], border=thin_border) current_row += 1 current_row += 1 set_cell_content('A', current_row, u'资源使用') current_row += 1 set_row_content(current_row, [u'虚拟机名称', u'cpu使用率', u'内存使用率', u'硬盘使用率'], border=thin_border) current_row += 1 resource_use = system['resource_use_rate'] for index, name in enumerate(resource_use["names"]): cpu_use_rate = resource_use["cpu_use_rates"][index] memory_use_rate = resource_use["memory_use_rates"][index] disk_use_rate = resource_use["disk_use_rates"][index] set_row_content(current_row, [name, cpu_use_rate, memory_use_rate, disk_use_rate], border=thin_border) current_row += 1 # 调整column宽度 ws.column_dimensions["A"].width = 19.17 ws.column_dimensions["B"].width = 24 ws.column_dimensions["C"].width = 17.50 ws.column_dimensions["D"].width = 21.50 # 调整row高度 ws.row_dimensions[1].height = 19 ws.row_dimensions[2].height = 98 for index in range(3, current_row): ws.row_dimensions[index].height = 15 # 大边框 def change_cell_border(cell, top=None, left=None, right=None, bottom=None): border = cell.border.copy() if top: border.top = top if left: border.left = left if right: border.right = right if bottom: border.bottom = bottom cell.border = border for column in ('A', 'B', 'C', 'D'): change_cell_border(ws['%s%s' % (column, 1)], top=medium_style) change_cell_border(ws['%s%s' % (column, current_row - 1)], bottom=medium_style) for row in range(1, current_row): change_cell_border(ws['A%s' % row], left=medium_style) change_cell_border(ws['D%s' % row], right=medium_style) # return wb return save_virtual_workbook(wb)
def output_excel(categories, count_eval, avg_iou, detection, unnecessary, pre_label_list, recalls, precisions): size = len(categories) # エクセル作成 wb = px.Workbook() # シート作成 ws_1 = wb.active ws_1 = wb.create_sheet(title='推論結果') # セル結合 ws_1.merge_cells(start_row=1, start_column=1, end_row=3, end_column=1) ws_1.merge_cells(start_row=1, start_column=2, end_row=1, end_column=2 + size) ws_1.merge_cells(start_row=2, start_column=2, end_row=3, end_column=2) ws_1.merge_cells(start_row=1, start_column=3 + size, end_row=1, end_column=4 + size * 2) ws_1.merge_cells(start_row=2, start_column=3, end_row=2, end_column=2 + size) ws_1.merge_cells(start_row=2, start_column=3 + size, end_row=2, end_column=2 + size * 2) ws_1.merge_cells(start_row=2, start_column=3 + size * 2, end_row=3, end_column=3 + size * 2) ws_1.merge_cells(start_row=2, start_column=4 + size * 2, end_row=3, end_column=4 + size * 2) ws_1.merge_cells(start_row=1, start_column=5 + size * 2, end_row=3, end_column=5 + size * 2) ws_1.merge_cells(start_row=1, start_column=6 + size * 2, end_row=3, end_column=6 + size * 2) ws_1.merge_cells(start_row=1, start_column=7 + size * 2, end_row=3, end_column=7 + size * 2) ws_1.merge_cells(start_row=1, start_column=8 + size * 2, end_row=3, end_column=8 + size * 2) # ヘッダーを記述 ws_1.cell(row=1, column=1).value = 'ラベル名' ws_1.cell(row=1, column=2).value = '正解ボックス' ws_1.cell(row=1, column=3 + size).value = '推論ボックス' ws_1.cell(row=2, column=2).value = '未検出' ws_1.cell(row=2, column=3).value = '検出' ws_1.cell(row=2, column=3 + size).value = '検出' ws_1.cell(row=2, column=3 + size * 2).value = '過検出' ws_1.cell(row=2, column=4 + size * 2).value = '不要' ws_1.cell(row=1, column=5 + size * 2).value = '平均IOU' ws_1.cell(row=1, column=6 + size * 2).value = '再現率(Recall)' ws_1.cell(row=1, column=7 + size * 2).value = '適合率(Precision)' ws_1.cell(row=1, column=8 + size * 2).value = 'F値(F-measure)' # ラベル名を記述 for i, label in enumerate(categories): ws_1.cell(row=4 + i, column=1).value = label ws_1.cell(row=3, column=3 + i).value = label ws_1.cell(row=3, column=3 + size + i).value = label # 評価データを記述 for i, box in enumerate(count_eval): for j, num in enumerate(box): ws_1.cell(row=4 + i, column=2 + j).value = num # 平均IOUを記述 for i, iou in enumerate(avg_iou): ws_1.cell(row=4 + i, column=5 + size * 2).value = iou #再現率、適合率、F値を記述 for i in range(len(recalls)): ws_1.cell(row=4 + i, column=6 + size * 2).value = recalls[i] ws_1.cell(row=4 + i, column=7 + size * 2).value = precisions[i] if recalls[i] != 0 and precisions[i] != 0: fmeasure = 2 * recalls[i] * precisions[i] / (recalls[i] + precisions[i]) else: fmeasure = 0 ws_1.cell(row=4 + i, column=8 + size * 2).value = fmeasure # アルファベットのリストを生成 alphabet_list = [chr(i) for i in range(97, 105 + size * 2)] # セル幅を広めに修正 # for i, c in enumerate(alphabet_list): # ws_1.column_dimensions[c].width = 14 # 文字位置を変更 for i in range(1, 9 + size * 2): for j in range(1, size + 4): ws_1.cell(row = j, column = i).alignment = \ Alignment(horizontal = 'center', vertical = 'center') # 枠線を生成 for i in range(1, 9 + size * 2): for j in range(1, size + 4): ws_1.cell(row = j, column = i).border = \ borders.Border(top = borders.Side(style = borders.BORDER_THIN, color = '000000'), left = borders.Side(style = borders.BORDER_THIN, color = '000000'), right = borders.Side(style = borders.BORDER_THIN, color = '000000'), bottom = borders.Side(style = borders.BORDER_THIN, color = '000000')) # フォントを変更 font = Font(name='メイリオ') for i in range(1, 9 + size * 2): for j in range(1, size + 4): ws_1.cell(row=j, column=i).font = font # シート作成 ws_2 = wb.create_sheet(title='IOU詳細') # セル結合 ws_2.merge_cells(start_row=1, start_column=7, end_row=2, end_column=7) ws_2.merge_cells(start_row=1, start_column=8, end_row=1, end_column=10) ws_2.merge_cells(start_row=1, start_column=11, end_row=1, end_column=13) # ヘッダーを記述 ws_2.cell(row=1, column=1).value = '画像' ws_2.cell(row=1, column=2).value = '正解ラベル' ws_2.cell(row=1, column=3).value = '推論ラベル' ws_2.cell(row=1, column=4).value = 'IOU' ws_2.cell(row=1, column=5).value = 'スコア' ws_2.cell(row=1, column=7).value = 'ラベル名' ws_2.cell(row=1, column=8).value = '検出' ws_2.cell(row=1, column=11).value = '過検出' ws_2.cell(row=2, column=8).value = '0.6未満' ws_2.cell(row=2, column=9).value = '0.6以上0.7未満' ws_2.cell(row=2, column=10).value = '0.7以上' ws_2.cell(row=2, column=11).value = '0.6未満' ws_2.cell(row=2, column=12).value = '0.6以上0.7未満' ws_2.cell(row=2, column=13).value = '0.7以上' # データを記述 for i, data in enumerate(detection): ws_2.cell(row=2 + i, column=1).value = data[0] ws_2.cell(row=2 + i, column=2).value = data[1] ws_2.cell(row=2 + i, column=3).value = data[2] ws_2.cell(row=2 + i, column=4).value = data[5] ws_2.cell(row=2 + i, column=5).value = data[6] cnt = 0 for i, x in enumerate(pre_label_list): switch = 0 for j, y in enumerate(detection): if (x[0] == y[0] and x[1] == y[2] and x[6] == y[4] and x[7] == y[6]): switch = 1 if (switch == 0): ws_2.cell(row=len(detection) + 2 + cnt, column=1).value = x[0] ws_2.cell(row=len(detection) + 2 + cnt, column=3).value = x[1] ws_2.cell(row=len(detection) + 2 + cnt, column=5).value = x[7] cnt += 1 iou_split = [[0 for i in range(7)] for j in range(len(categories))] for i, label in enumerate(categories): iou_split[i][0] = label for data in detection: if (label == data[1] and data[1] == data[2] and data[5] < 0.6): iou_split[i][1] += 1 elif (label == data[1] and data[1] == data[2] and data[5] >= 0.7): iou_split[i][3] += 1 elif (label == data[1] and data[1] == data[2] and data[5] >= 0.6 and data[5] < 0.7): iou_split[i][2] += 1 if (label == data[1] and data[1] != data[2] and data[5] < 0.6): iou_split[i][4] += 1 elif (label == data[1] and data[1] != data[2] and data[5] >= 0.7): iou_split[i][6] += 1 elif (label == data[1] and data[1] != data[2] and data[5] >= 0.6 and data[5] < 0.7): iou_split[i][5] += 1 # データを記述 for i, data in enumerate(iou_split): ws_2.cell(row=3 + i, column=7).value = data[0] ws_2.cell(row=3 + i, column=8).value = data[1] ws_2.cell(row=3 + i, column=9).value = data[2] ws_2.cell(row=3 + i, column=10).value = data[3] ws_2.cell(row=3 + i, column=11).value = data[4] ws_2.cell(row=3 + i, column=12).value = data[5] ws_2.cell(row=3 + i, column=13).value = data[6] # アルファベットのリストを生成 alphabet_list = [chr(i) for i in range(97, 110)] # セル幅を広めに修正 for i, c in enumerate(alphabet_list): ws_2.column_dimensions[c].width = 14 # 文字位置を変更 for i in range(1, 6): ws_2.cell(row = 1, column = i).alignment = \ Alignment(horizontal = 'center', vertical = 'center') for i in range(6, 14): for j in range(1, size + 3): ws_2.cell(row = j, column = i).alignment = \ Alignment(horizontal = 'center', vertical = 'center') # 枠線を生成 for i in range(7, 14): for j in range(1, size + 3): ws_2.cell(row = j, column = i).border = \ borders.Border(top = borders.Side(style = borders.BORDER_THIN, color = '000000'), left = borders.Side(style = borders.BORDER_THIN, color = '000000'), right = borders.Side(style = borders.BORDER_THIN, color = '000000'), bottom = borders.Side(style = borders.BORDER_THIN, color = '000000')) # フォントを変更 for i in range(1, 14): for j in range(1, len(detection) + 1): ws_2.cell(row=j, column=i).font = font # シート削除 wb.remove(wb['Sheet']) # エクセル保存 save_excel_path = os.path.join(args.pre_xml, 'evaluation.xlsx') wb.save(save_excel_path) print('Successful Completion ({})'.format(save_excel_path))
from openpyxl.styles import PatternFill, Alignment, Font # colorcodes BLUECOLORCODE = 'FF61D2FF' GREENCOLORCODE = 'FF6DE992' YELLOWCOLORCODE = 'FFFFFF52' PURPLECOLORCODE = 'FFE057FF' GRAYCOLORCODE = 'FFC8C8C8' thin_border = xlborders.Border(left=xlborders.Side(style='thin', color=GRAYCOLORCODE), right=xlborders.Side(style='thin', color=GRAYCOLORCODE), top=xlborders.Side(style='thin', color=GRAYCOLORCODE), bottom=xlborders.Side(style='thin', color=GRAYCOLORCODE), vertical=xlborders.Side(style='thin', color=GRAYCOLORCODE), horizontal=xlborders.Side(style='thin', color=GRAYCOLORCODE)) # %% create excelfile def createProfitExcel(coinList, path, minDate, maxDate, currency='EUR', taxyearlimit=1, useWalletTaxYearLimit=True,
class Command(BaseCommand): TEMPLATE = 'templates/ecommerce/template.xlsx' NAME = 'pricelist.xlsx' SHEET_TITLE = 'Прайс Shopelectro' CATEGORY_FILL = openpyxl.styles.PatternFill( start_color='F4FEFD', end_color='F4FEFD', fill_type='solid' ) BUY_FILL = openpyxl.styles.PatternFill( start_color='FEFEF0', end_color='FEFEF0', fill_type='solid' ) THIN_BORDER = borders.Border( top=borders.Side(style='thin'), right=borders.Side(style='thin'), bottom=borders.Side(style='thin'), left=borders.Side(style='thin') ) CURRENT_ROW = '9' # Start of catalog section in file. cell = namedtuple('cell', ['row', 'col']) BAD_STYLED_CELLS = ['D5', 'E5', 'D6', 'G8'] def __init__(self, *args, **kwargs): super(Command, self).__init__(*args, **kwargs) self.file, self.sheet = self.load_file_and_sheet() def handle(self, *args, **options): """Open template's file and start proceeding it.""" self.set_collapse_controls() self.fill_header() self.write_catalog() self.hide_formulas() self.set_styles() base_dir = settings.ASSETS_DIR self.file.save(os.path.join(base_dir, self.NAME)) def set_styles(self): for cell in self.BAD_STYLED_CELLS: self.sheet[cell].border = self.THIN_BORDER def set_collapse_controls(self): """ Place collapse buttons above rows. Collapse controls looks like this: http://prntscr.com/clf9xh. # Ignore InvalidLinkBear Doc link: https://goo.gl/nR5pLO """ self.sheet.sheet_properties.outlinePr.summaryBelow = False def increase_row(self): self.CURRENT_ROW = str(int(self.CURRENT_ROW) + 1) return self.CURRENT_ROW def get_row(self, row_number): return self.sheet.row_dimensions[int(row_number)] def load_file_and_sheet(self): """ Load template file into openpyxl. Return tuple with opened openpyxl file's object and active price sheet. """ file = openpyxl.load_workbook(os.path.join( settings.BASE_DIR, self.TEMPLATE)) return file, file.get_sheet_by_name('Прайслист') def fill_header(self): """Fill header of a sheet with date and title.""" date_cell = 'C5' self.sheet.title = self.SHEET_TITLE self.sheet[date_cell] = datetime.date.strftime( datetime.date.today(), '%d.%m.%Y') def hide_formulas(self): """Hide formulas for calculating totals.""" self.sheet.column_dimensions.group('H', 'K', hidden=True, outline_level=0) def write_catalog(self): """Write categories and products to sheet.""" categories = Category.objects.active().order_by('name').filter(children=None) for category in categories.iterator(): self.write_category_with_products(category) def write_category_with_products(self, category): """Write category line and beside that - all of products in this category.""" def hide_row(row): row.hidden = True row.outlineLevel = 1 def collapse_row(row): row.collapsed = True def write_product_rows(): """Write products lines.""" sheet = self.sheet products = Product.objects.filter(category=category, page__is_active=True) for product in products.iterator(): product_start = 'A' + self.CURRENT_ROW sheet[product_start] = product.name sheet[product_start].font = Font(color=colors.BLUE) sheet[product_start].hyperlink = settings.BASE_URL + product.url sheet[product_start].border = self.THIN_BORDER prices = [ product.price, product.wholesale_small, product.wholesale_medium, product.wholesale_large, ] for price, total in zip('CDEF', 'HIJK'): sheet[price + self.CURRENT_ROW] = prices.pop(0) sheet[total + self.CURRENT_ROW] = ( '={0}{1}*G{1}'.format(price, self.CURRENT_ROW) ) sheet[price + self.CURRENT_ROW].border = self.THIN_BORDER sheet['G' + self.CURRENT_ROW].fill = self.BUY_FILL sheet['G' + self.CURRENT_ROW].border = self.THIN_BORDER hide_row(self.get_row(self.CURRENT_ROW)) self.increase_row() def write_category_row(): """Merge category line into one cell and write to it.""" sheet = self.sheet collapse_row(self.get_row(self.CURRENT_ROW)) category_start = 'A' + self.CURRENT_ROW category_line = '{}:{}'.format( category_start, 'G' + self.CURRENT_ROW) sheet.merge_cells(category_line) sheet[category_start] = category.name sheet[category_start].fill = self.CATEGORY_FILL self.increase_row() write_category_row() write_product_rows()