@@ 16,14 16,113 @@
# You should have received a copy of the GNU General Public License
# along with ade. If not, see <http://www.gnu.org/licenses/>.
+from decimal import Decimal
+
import pandas as pd
+from lib import reports
import xlwings as xw
+from xlwings.constants import BordersIndex, HAlign, LineStyle
def adhoc_data(sht: xw.main.Sheet, rng: xw.main.Range, data: pd.DataFrame):
- sht.tables.add(rng.expand("table"), table_style_name="TableStyleLight10")
+ sht.tables.add(
+ source=rng.expand("table"), name="df", table_style_name="TableStyleLight10"
+ )
+ sht.tables["df"].show_autofilter = True
+
+
+def adhoc_pivot(
+ app: xw.main.App,
+ book: xw.main.Book,
+ sht: xw.main.Sheet,
+ rng: xw.main.Range,
+ data: pd.DataFrame,
+ pivot_struct: reports.PivotArgs,
+):
+ sht.select()
+ app.api.ActiveWindow.DisplayGridlines = False
+
+ freeze_row = rng.end("down").offset(row_offset=1).row
+ sht.range(f"{freeze_row}:{freeze_row}").select()
+ app.api.ActiveWindow.FreezePanes = True
+ sht.range((freeze_row, 1)).select()
+
+ title = rng.offset(row_offset=-1)
+ title.font.size = 16
+ title.value = pivot_struct.name
+
+ header_corner = rng.end("down")
+ header = header_corner.expand("right")
+ header.api.AutoFilter(Field=1)
+ header.expand("table").autofit()
+
+ number_of_index_columns = len(data.index[0])
+ index_header_end = header_corner.offset(column_offset=(number_of_index_columns - 1))
+
+ index_header = sht.range(header_corner, index_header_end)
+ margin_header = sht.range(
+ header.last_cell.offset(column_offset=-(len(pivot_struct.aggfunc) - 1)),
+ header.last_cell,
+ )
+ value_header = sht.range(
+ (margin_header.row, index_header.last_cell.offset(column_offset=1).column),
+ (margin_header.row, margin_header.column - 1),
+ )
+
+ value_header.expand("right").api.HorizontalAlignment = HAlign.xlHAlignCenter
+ sht.range(title, header.last_cell).font.bold = True
+
+ last_row = index_header.expand("down").last_cell.row
+
+ # subtotal formatting must loop through cell values to determine if its necessary
+ gradient_range = (242, 121)
+ number_of_subs = len(pivot_struct.subs)
+ gradient_step = {5: 20, 10: 10, 20: 5, 0: 1}
+ for steps, percent in gradient_step.items():
+ if number_of_subs <= steps or steps == 0:
+ gradient = int(gradient_range[1] * Decimal(f"0.{percent}"))
+ break
+ for idx, head_cell in enumerate(reversed(index_header)):
+ step = idx + 1
+ if head_cell.value in pivot_struct.subs:
+ value_range = sht.range(head_cell, (last_row, head_cell.column))
+ for value_cell in value_range:
+ str_value = str(value_cell.value)
+ if " - Total" in str_value:
+ subtotal_range = sht.range(
+ value_cell.address, (value_cell.row, header.last_cell.column)
+ )
+ subtotal_range.color = tuple(
+ [gradient_range[0] - (gradient * step)] * 3
+ )
+ subtotal_range.font.italic = True
+ elif value_cell.value == "~All":
+ total_range = sht.range(
+ value_cell.address, (value_cell.row, margin_header.column - 1)
+ )
+ total_range.font.bold = True
+ total_range.api.Borders(
+ BordersIndex.xlEdgeTop
+ ).LineStyle = LineStyle.xlContinuous
-def adhoc_pivot(sht: xw.main.Sheet, rng: xw.main.Range, data: pd.DataFrame):
- pass
+ try:
+ first_margin_range = sht.range(
+ (margin_header.offset(row_offset=1).row, margin_header.column),
+ (total_range.row, margin_header.column),
+ )
+ except UnboundLocalError:
+ first_margin_range = sht.range(
+ (margin_header.offset(row_offset=1).row, margin_header.column),
+ (index_header.expand("down").row, margin_header.column),
+ )
+ finally:
+ first_margin_range.font.bold = True
+ first_margin_border = sht.range(
+ first_margin_range.address.split(":")[0],
+ (first_margin_range.last_cell.row - 1, first_margin_range.column),
+ )
+ first_margin_border.api.Borders(
+ BordersIndex.xlEdgeLeft
+ ).LineStyle = LineStyle.xlContinuous
@@ 85,6 85,7 @@ class DataStructure:
structure: SheetStructure
data: pd.DataFrame
output: Output
+ pivot: Optional[rep.PivotArgs]
class _Parameters:
@@ 2048,7 2049,10 @@ class Compile:
if include_data or not (self.report.pivot or pivot_args):
self.data = {
"df": DataStructure(
- SheetStructure("Sheet1", "A1"), self.output.df, self.output
+ SheetStructure("Sheet1", "A1"),
+ self.output.df,
+ self.output,
+ None,
)
}
@@ 2116,6 2120,7 @@ class Compile:
margins=collection.margins,
),
self.output,
+ collection,
)
else:
self.data[collection.name] = DataStructure(
@@ 2128,6 2133,7 @@ class Compile:
margins=collection.margins,
),
self.output,
+ collection,
)
def equipment_report(
@@ 2158,7 2164,10 @@ class Compile:
# Sets the structure of where data gets pasted in the appropriate template
data = {
"df": DataStructure(
- SheetStructure("Summary", "A8"), self.output.df.copy(), self.output
+ SheetStructure("Summary", "A8"),
+ self.output.df.copy(),
+ self.output,
+ None,
)
}
@@ 2381,7 2390,7 @@ class Compile:
data = {
"pivot": DataStructure(
- SheetStructure("ABI Data", "D1"), pivot, self.output
+ SheetStructure("ABI Data", "D1"), pivot, self.output, None
),
}
@@ 2453,16 2462,16 @@ class Compile:
data = {
"revenue_location": DataStructure(
- SheetStructure("RevenueByLocation", "A1"), rev_lc, self.output
+ SheetStructure("RevenueByLocation", "A1"), rev_lc, self.output, None
),
"revenue_product": DataStructure(
- SheetStructure("RevenueByProduct", "A1"), rev_pr, self.output
+ SheetStructure("RevenueByProduct", "A1"), rev_pr, self.output, None
),
"dtp_location": DataStructure(
- SheetStructure("DaysToPay", "A1"), dtp_lc, self.output
+ SheetStructure("DaysToPay", "A1"), dtp_lc, self.output, None
),
"revenue_type": DataStructure(
- SheetStructure("RevenueByType", "A1"), rev_tp, self.output
+ SheetStructure("RevenueByType", "A1"), rev_tp, self.output, None
),
}
@@ 2550,7 2559,7 @@ class Compile:
).reset_index(drop=True)
df.index.name = "Item"
- data = {"df": DataStructure(SheetStructure("ade", "B3"), df, self.output)}
+ data = {"df": DataStructure(SheetStructure("ade", "B3"), df, self.output, None)}
return data
@@ 2609,10 2618,10 @@ class Compile:
data = {
"listing": DataStructure(
- SheetStructure("Summary", "A2"), listing, self.output
+ SheetStructure("Summary", "A2"), listing, self.output, None
),
"values": DataStructure(
- SheetStructure("Summary", "I1"), values, self.output
+ SheetStructure("Summary", "I1"), values, self.output, None
),
}
@@ 2758,15 2767,21 @@ class File:
if not data_struct.data.index.name:
data_struct.data.index.name = "index"
- sht = book.sheets[data_struct.structure.sheet]
+ sht_name = data_struct.structure.sheet
+ if sht_name not in [i.name for i in book.sheets]:
+ book.sheets.add(sht_name, after=book.sheets[-1])
+ sht = book.sheets[sht_name]
+
rng = sht.range(data_struct.structure.cell)
# full.expand("table").clear_contents()
rng.value = data_struct.data
# full.api.AutoFilter()
if self.report.formatting:
- if self.report.pivot:
- formatting.adhoc_pivot(sht, rng, data_struct.data)
+ if data_struct.pivot:
+ formatting.adhoc_pivot(
+ app, book, sht, rng, data_struct.data, data_struct.pivot
+ )
else:
formatting.adhoc_data(sht, rng, data_struct.data)