{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE DeriveGeneric #-}
module Codec.Xlsx.Writer.Internal.PivotTable
  ( PivotTableFiles(..)
  , renderPivotTableFiles
  ) where

import Data.ByteString.Lazy (ByteString)
import Data.List (elemIndex, transpose)
import Data.List.Extra (nubOrd)
import qualified Data.Map as M
import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
import Data.Text (Text)
import GHC.Generics (Generic)
import Safe (fromJustNote)
import Text.XML

import Codec.Xlsx.Types.Cell
import Codec.Xlsx.Types.Common
import Codec.Xlsx.Types.Internal
import Codec.Xlsx.Types.Internal.Relationships (odr)
import Codec.Xlsx.Types.PivotTable
import Codec.Xlsx.Types.PivotTable.Internal
import Codec.Xlsx.Writer.Internal

data PivotTableFiles = PivotTableFiles
  { PivotTableFiles -> ByteString
pvtfTable :: ByteString
  , PivotTableFiles -> ByteString
pvtfCacheDefinition :: ByteString
  , PivotTableFiles -> ByteString
pvtfCacheRecords :: ByteString
  } deriving (PivotTableFiles -> PivotTableFiles -> Bool
(PivotTableFiles -> PivotTableFiles -> Bool)
-> (PivotTableFiles -> PivotTableFiles -> Bool)
-> Eq PivotTableFiles
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PivotTableFiles -> PivotTableFiles -> Bool
$c/= :: PivotTableFiles -> PivotTableFiles -> Bool
== :: PivotTableFiles -> PivotTableFiles -> Bool
$c== :: PivotTableFiles -> PivotTableFiles -> Bool
Eq, Int -> PivotTableFiles -> ShowS
[PivotTableFiles] -> ShowS
PivotTableFiles -> String
(Int -> PivotTableFiles -> ShowS)
-> (PivotTableFiles -> String)
-> ([PivotTableFiles] -> ShowS)
-> Show PivotTableFiles
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PivotTableFiles] -> ShowS
$cshowList :: [PivotTableFiles] -> ShowS
show :: PivotTableFiles -> String
$cshow :: PivotTableFiles -> String
showsPrec :: Int -> PivotTableFiles -> ShowS
$cshowsPrec :: Int -> PivotTableFiles -> ShowS
Show, (forall x. PivotTableFiles -> Rep PivotTableFiles x)
-> (forall x. Rep PivotTableFiles x -> PivotTableFiles)
-> Generic PivotTableFiles
forall x. Rep PivotTableFiles x -> PivotTableFiles
forall x. PivotTableFiles -> Rep PivotTableFiles x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep PivotTableFiles x -> PivotTableFiles
$cfrom :: forall x. PivotTableFiles -> Rep PivotTableFiles x
Generic)

data CacheDefinition = CacheDefinition
  { CacheDefinition -> CellRef
cdSourceRef :: CellRef
  , CacheDefinition -> Text
cdSourceSheet :: Text
  , CacheDefinition -> [CacheField]
cdFields :: [CacheField]
  } deriving (CacheDefinition -> CacheDefinition -> Bool
(CacheDefinition -> CacheDefinition -> Bool)
-> (CacheDefinition -> CacheDefinition -> Bool)
-> Eq CacheDefinition
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CacheDefinition -> CacheDefinition -> Bool
$c/= :: CacheDefinition -> CacheDefinition -> Bool
== :: CacheDefinition -> CacheDefinition -> Bool
$c== :: CacheDefinition -> CacheDefinition -> Bool
Eq, Int -> CacheDefinition -> ShowS
[CacheDefinition] -> ShowS
CacheDefinition -> String
(Int -> CacheDefinition -> ShowS)
-> (CacheDefinition -> String)
-> ([CacheDefinition] -> ShowS)
-> Show CacheDefinition
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CacheDefinition] -> ShowS
$cshowList :: [CacheDefinition] -> ShowS
show :: CacheDefinition -> String
$cshow :: CacheDefinition -> String
showsPrec :: Int -> CacheDefinition -> ShowS
$cshowsPrec :: Int -> CacheDefinition -> ShowS
Show, (forall x. CacheDefinition -> Rep CacheDefinition x)
-> (forall x. Rep CacheDefinition x -> CacheDefinition)
-> Generic CacheDefinition
forall x. Rep CacheDefinition x -> CacheDefinition
forall x. CacheDefinition -> Rep CacheDefinition x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep CacheDefinition x -> CacheDefinition
$cfrom :: forall x. CacheDefinition -> Rep CacheDefinition x
Generic)

renderPivotTableFiles :: CellMap -> Int -> PivotTable -> PivotTableFiles
renderPivotTableFiles :: CellMap -> Int -> PivotTable -> PivotTableFiles
renderPivotTableFiles CellMap
cm Int
cacheId PivotTable
t = PivotTableFiles :: ByteString -> ByteString -> ByteString -> PivotTableFiles
PivotTableFiles {ByteString
pvtfCacheRecords :: ByteString
pvtfCacheDefinition :: ByteString
pvtfTable :: ByteString
pvtfCacheRecords :: ByteString
pvtfCacheDefinition :: ByteString
pvtfTable :: ByteString
..}
  where
    pvtfTable :: ByteString
pvtfTable = RenderSettings -> Document -> ByteString
renderLBS RenderSettings
forall a. Default a => a
def (Document -> ByteString) -> Document -> ByteString
forall a b. (a -> b) -> a -> b
$ Int -> CacheDefinition -> PivotTable -> Document
ptDefinitionDocument Int
cacheId CacheDefinition
cache PivotTable
t
    cache :: CacheDefinition
cache = CellMap -> PivotTable -> CacheDefinition
generateCache CellMap
cm PivotTable
t
    (Document
cacheDoc, Document
cacheRecordsDoc) = CacheDefinition -> (Document, Document)
writeCache CacheDefinition
cache
    pvtfCacheDefinition :: ByteString
pvtfCacheDefinition = RenderSettings -> Document -> ByteString
renderLBS RenderSettings
forall a. Default a => a
def Document
cacheDoc
    pvtfCacheRecords :: ByteString
pvtfCacheRecords = RenderSettings -> Document -> ByteString
renderLBS RenderSettings
forall a. Default a => a
def Document
cacheRecordsDoc

ptDefinitionDocument :: Int -> CacheDefinition -> PivotTable -> Document
ptDefinitionDocument :: Int -> CacheDefinition -> PivotTable -> Document
ptDefinitionDocument Int
cacheId CacheDefinition
cache PivotTable
t =
    Text -> Element -> Document
documentFromElement Text
"Pivot table generated by xlsx" (Element -> Document) -> Element -> Document
forall a b. (a -> b) -> a -> b
$
    Name -> Int -> CacheDefinition -> PivotTable -> Element
ptDefinitionElement Name
"pivotTableDefinition" Int
cacheId CacheDefinition
cache PivotTable
t

ptDefinitionElement :: Name -> Int -> CacheDefinition -> PivotTable -> Element
ptDefinitionElement :: Name -> Int -> CacheDefinition -> PivotTable -> Element
ptDefinitionElement Name
nm Int
cacheId CacheDefinition
cache PivotTable {Bool
[DataField]
[PositionedField]
[PivotFieldInfo]
Text
CellRef
_pvtSrcRef :: PivotTable -> CellRef
_pvtSrcSheet :: PivotTable -> Text
_pvtLocation :: PivotTable -> CellRef
_pvtOutlineData :: PivotTable -> Bool
_pvtOutline :: PivotTable -> Bool
_pvtColumnGrandTotals :: PivotTable -> Bool
_pvtRowGrandTotals :: PivotTable -> Bool
_pvtFields :: PivotTable -> [PivotFieldInfo]
_pvtDataFields :: PivotTable -> [DataField]
_pvtColumnFields :: PivotTable -> [PositionedField]
_pvtRowFields :: PivotTable -> [PositionedField]
_pvtDataCaption :: PivotTable -> Text
_pvtName :: PivotTable -> Text
_pvtSrcRef :: CellRef
_pvtSrcSheet :: Text
_pvtLocation :: CellRef
_pvtOutlineData :: Bool
_pvtOutline :: Bool
_pvtColumnGrandTotals :: Bool
_pvtRowGrandTotals :: Bool
_pvtFields :: [PivotFieldInfo]
_pvtDataFields :: [DataField]
_pvtColumnFields :: [PositionedField]
_pvtRowFields :: [PositionedField]
_pvtDataCaption :: Text
_pvtName :: Text
..} =
  Name -> [(Name, Text)] -> [Element] -> Element
elementList Name
nm [(Name, Text)]
attrs [Element]
elements
  where
    attrs :: [(Name, Text)]
attrs =
      [Maybe (Name, Text)] -> [(Name, Text)]
forall a. [Maybe a] -> [a]
catMaybes
        [ Name
"colGrandTotals" Name -> Maybe Bool -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Bool -> Maybe Bool
justFalse Bool
_pvtColumnGrandTotals
        , Name
"rowGrandTotals" Name -> Maybe Bool -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Bool -> Maybe Bool
justFalse Bool
_pvtRowGrandTotals
        , Name
"outline" Name -> Maybe Bool -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Bool -> Maybe Bool
justTrue Bool
_pvtOutline
        , Name
"outlineData" Name -> Maybe Bool -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Bool -> Maybe Bool
justTrue Bool
_pvtOutlineData
        ] [(Name, Text)] -> [(Name, Text)] -> [(Name, Text)]
forall a. [a] -> [a] -> [a]
++
      [ Name
"name" Name -> Text -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Text
_pvtName
      , Name
"dataCaption" Name -> Text -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Text
_pvtDataCaption
      , Name
"cacheId" Name -> Int -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Int
cacheId
      , Name
"dataOnRows" Name -> Bool -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= (PositionedField
DataPosition PositionedField -> [PositionedField] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [PositionedField]
_pvtRowFields)
      ]
    elements :: [Element]
elements = [Element
location, Element
pivotFields, Element
rowFields, Element
colFields, Element
dataFields]
    location :: Element
location =
      Name -> [(Name, Text)] -> Element
leafElement
        Name
"location"
        [ Name
"ref" Name -> CellRef -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= CellRef
_pvtLocation
          -- TODO : set proper
        , Name
"firstHeaderRow" Name -> Int -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= (Int
1 :: Int)
        , Name
"firstDataRow" Name -> Int -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= (Int
2 :: Int)
        , Name
"firstDataCol" Name -> Int -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= (Int
1 :: Int)
        ]
    name2x :: Map PivotFieldName Int
name2x = [(PivotFieldName, Int)] -> Map PivotFieldName Int
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList ([(PivotFieldName, Int)] -> Map PivotFieldName Int)
-> [(PivotFieldName, Int)] -> Map PivotFieldName Int
forall a b. (a -> b) -> a -> b
$ [PivotFieldName] -> [Int] -> [(PivotFieldName, Int)]
forall a b. [a] -> [b] -> [(a, b)]
zip ((PivotFieldInfo -> Maybe PivotFieldName)
-> [PivotFieldInfo] -> [PivotFieldName]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe PivotFieldInfo -> Maybe PivotFieldName
_pfiName [PivotFieldInfo]
_pvtFields) [Int
0 ..]
    mapFieldToX :: PivotFieldName -> Int
mapFieldToX PivotFieldName
f = String -> Maybe Int -> Int
forall a. Partial => String -> Maybe a -> a
fromJustNote String
"no field" (Maybe Int -> Int) -> Maybe Int -> Int
forall a b. (a -> b) -> a -> b
$ PivotFieldName -> Map PivotFieldName Int -> Maybe Int
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup PivotFieldName
f Map PivotFieldName Int
name2x
    pivotFields :: Element
pivotFields = Name -> [Element] -> Element
elementListSimple Name
"pivotFields" ([Element] -> Element) -> [Element] -> Element
forall a b. (a -> b) -> a -> b
$ (PivotFieldInfo -> Element) -> [PivotFieldInfo] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
map PivotFieldInfo -> Element
pFieldEl [PivotFieldInfo]
_pvtFields
    maybeFieldIn :: Maybe PivotFieldName -> t PositionedField -> Bool
maybeFieldIn Maybe PivotFieldName
Nothing t PositionedField
_ = Bool
False
    maybeFieldIn (Just PivotFieldName
name) t PositionedField
positions = PivotFieldName -> PositionedField
FieldPosition PivotFieldName
name PositionedField -> t PositionedField -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` t PositionedField
positions
    pFieldEl :: PivotFieldInfo -> Element
pFieldEl PivotFieldInfo { _pfiName :: PivotFieldInfo -> Maybe PivotFieldName
_pfiName = Maybe PivotFieldName
fName
                            , _pfiOutline :: PivotFieldInfo -> Bool
_pfiOutline = Bool
outline
                            , _pfiSortType :: PivotFieldInfo -> FieldSortType
_pfiSortType = FieldSortType
sortType
                            , _pfiHiddenItems :: PivotFieldInfo -> [CellValue]
_pfiHiddenItems = [CellValue]
hidden
                            }
      | Maybe PivotFieldName
fName Maybe PivotFieldName -> [PositionedField] -> Bool
forall (t :: * -> *).
Foldable t =>
Maybe PivotFieldName -> t PositionedField -> Bool
`maybeFieldIn` [PositionedField]
_pvtRowFields =
        Maybe PivotFieldName
-> Bool -> Text -> [CellValue] -> FieldSortType -> Element
forall a a (t :: * -> *).
(ToAttrVal a, ToAttrVal a, Foldable t) =>
Maybe PivotFieldName
-> a -> a -> t CellValue -> FieldSortType -> Element
pFieldEl' Maybe PivotFieldName
fName Bool
outline (Text
"axisRow" :: Text) [CellValue]
hidden FieldSortType
sortType
      | Maybe PivotFieldName
fName Maybe PivotFieldName -> [PositionedField] -> Bool
forall (t :: * -> *).
Foldable t =>
Maybe PivotFieldName -> t PositionedField -> Bool
`maybeFieldIn` [PositionedField]
_pvtColumnFields =
        Maybe PivotFieldName
-> Bool -> Text -> [CellValue] -> FieldSortType -> Element
forall a a (t :: * -> *).
(ToAttrVal a, ToAttrVal a, Foldable t) =>
Maybe PivotFieldName
-> a -> a -> t CellValue -> FieldSortType -> Element
pFieldEl' Maybe PivotFieldName
fName Bool
outline (Text
"axisCol" :: Text) [CellValue]
hidden FieldSortType
sortType
      | Bool
otherwise =
        Name -> [(Name, Text)] -> Element
leafElement Name
"pivotField" ([(Name, Text)] -> Element) -> [(Name, Text)] -> Element
forall a b. (a -> b) -> a -> b
$
        [ Name
"dataField" Name -> Bool -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Bool
True
        , Name
"showAll" Name -> Bool -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Bool
False
        , Name
"outline" Name -> Bool -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Bool
outline] [(Name, Text)] -> [(Name, Text)] -> [(Name, Text)]
forall a. [a] -> [a] -> [a]
++
        [Maybe (Name, Text)] -> [(Name, Text)]
forall a. [Maybe a] -> [a]
catMaybes [Name
"name" Name -> Maybe PivotFieldName -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Maybe PivotFieldName
fName]
    pFieldEl' :: Maybe PivotFieldName
-> a -> a -> t CellValue -> FieldSortType -> Element
pFieldEl' Maybe PivotFieldName
fName a
outline a
axis t CellValue
hidden FieldSortType
sortType =
      Name -> [(Name, Text)] -> [Element] -> Element
elementList
        Name
"pivotField"
        ([ Name
"axis" Name -> a -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= a
axis
         , Name
"showAll" Name -> Bool -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Bool
False
         , Name
"outline" Name -> a -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= a
outline
         ] [(Name, Text)] -> [(Name, Text)] -> [(Name, Text)]
forall a. [a] -> [a] -> [a]
++
         [Maybe (Name, Text)] -> [(Name, Text)]
forall a. [Maybe a] -> [a]
catMaybes [ Name
"name" Name -> Maybe PivotFieldName -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Maybe PivotFieldName
fName
                   , Name
"sortType" Name -> Maybe FieldSortType -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? FieldSortType -> FieldSortType -> Maybe FieldSortType
forall a. Eq a => a -> a -> Maybe a
justNonDef FieldSortType
FieldSortManual FieldSortType
sortType])
        [ Name -> [Element] -> Element
elementListSimple Name
"items" ([Element] -> Element) -> [Element] -> Element
forall a b. (a -> b) -> a -> b
$
          Maybe PivotFieldName -> t CellValue -> [Element]
forall (t :: * -> *).
Foldable t =>
Maybe PivotFieldName -> t CellValue -> [Element]
items Maybe PivotFieldName
fName t CellValue
hidden [Element] -> [Element] -> [Element]
forall a. [a] -> [a] -> [a]
++
          [Name -> [(Name, Text)] -> Element
leafElement Name
"item" [Name
"t" Name -> Text -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= (Text
"default" :: Text)]]
        ]
    items :: Maybe PivotFieldName -> t CellValue -> [Element]
items Maybe PivotFieldName
Nothing t CellValue
_ = []
    items (Just PivotFieldName
fName) t CellValue
hidden =
      [ Int -> CellValue -> t CellValue -> Element
forall (t :: * -> *) a.
(Foldable t, Eq a) =>
Int -> a -> t a -> Element
itemEl Int
x CellValue
item t CellValue
hidden
      | (Int
x, CellValue
item) <- [Int] -> [CellValue] -> [(Int, CellValue)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0 ..] ([CellValue] -> [(Int, CellValue)])
-> (Maybe [CellValue] -> [CellValue])
-> Maybe [CellValue]
-> [(Int, CellValue)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [CellValue] -> Maybe [CellValue] -> [CellValue]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [CellValue] -> [(Int, CellValue)])
-> Maybe [CellValue] -> [(Int, CellValue)]
forall a b. (a -> b) -> a -> b
$ PivotFieldName
-> Map PivotFieldName [CellValue] -> Maybe [CellValue]
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup PivotFieldName
fName Map PivotFieldName [CellValue]
cachedItems
      ]
    itemEl :: Int -> a -> t a -> Element
itemEl Int
x a
item t a
hidden =
      Name -> [(Name, Text)] -> Element
leafElement Name
"item" ([(Name, Text)] -> Element) -> [(Name, Text)] -> Element
forall a b. (a -> b) -> a -> b
$
      [Name
"x" Name -> Int -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= (Int
x :: Int)] [(Name, Text)] -> [(Name, Text)] -> [(Name, Text)]
forall a. [a] -> [a] -> [a]
++ [Maybe (Name, Text)] -> [(Name, Text)]
forall a. [Maybe a] -> [a]
catMaybes [Name
"h" Name -> Maybe Bool -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Bool -> Maybe Bool
justTrue (a
item a -> t a -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` t a
hidden)]
    cachedItems :: Map PivotFieldName [CellValue]
cachedItems =
      [(PivotFieldName, [CellValue])] -> Map PivotFieldName [CellValue]
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList ([(PivotFieldName, [CellValue])] -> Map PivotFieldName [CellValue])
-> [(PivotFieldName, [CellValue])]
-> Map PivotFieldName [CellValue]
forall a b. (a -> b) -> a -> b
$ [(PivotFieldName
cfName, [CellValue]
cfItems) | CacheField {[CellValue]
PivotFieldName
cfItems :: CacheField -> [CellValue]
cfName :: CacheField -> PivotFieldName
cfItems :: [CellValue]
cfName :: PivotFieldName
..} <- CacheDefinition -> [CacheField]
cdFields CacheDefinition
cache]
    rowFields :: Element
rowFields =
      Name -> [Element] -> Element
elementListSimple Name
"rowFields" ([Element] -> Element)
-> ([PositionedField] -> [Element]) -> [PositionedField] -> Element
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PositionedField -> Element) -> [PositionedField] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
map PositionedField -> Element
fieldEl ([PositionedField] -> Element) -> [PositionedField] -> Element
forall a b. (a -> b) -> a -> b
$
      if [DataField] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [DataField]
_pvtDataFields Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
1
        then [PositionedField]
_pvtRowFields
        else (PositionedField -> Bool) -> [PositionedField] -> [PositionedField]
forall a. (a -> Bool) -> [a] -> [a]
filter (PositionedField -> PositionedField -> Bool
forall a. Eq a => a -> a -> Bool
/= PositionedField
DataPosition) [PositionedField]
_pvtRowFields
    colFields :: Element
colFields = Name -> [Element] -> Element
elementListSimple Name
"colFields" ([Element] -> Element) -> [Element] -> Element
forall a b. (a -> b) -> a -> b
$ (PositionedField -> Element) -> [PositionedField] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
map PositionedField -> Element
fieldEl [PositionedField]
_pvtColumnFields
    fieldEl :: PositionedField -> Element
fieldEl PositionedField
p = Name -> [(Name, Text)] -> Element
leafElement Name
"field" [Name
"x" Name -> Int -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= PositionedField -> Int
fieldPos PositionedField
p]
    fieldPos :: PositionedField -> Int
fieldPos PositionedField
DataPosition = (-Int
2) :: Int
    fieldPos (FieldPosition PivotFieldName
f) = PivotFieldName -> Int
mapFieldToX PivotFieldName
f
    dataFields :: Element
dataFields = Name -> [Element] -> Element
elementListSimple Name
"dataFields" ([Element] -> Element) -> [Element] -> Element
forall a b. (a -> b) -> a -> b
$ (DataField -> Element) -> [DataField] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
map DataField -> Element
dFieldEl [DataField]
_pvtDataFields
    dFieldEl :: DataField -> Element
dFieldEl DataField {Text
ConsolidateFunction
PivotFieldName
_dfFunction :: DataField -> ConsolidateFunction
_dfName :: DataField -> Text
_dfField :: DataField -> PivotFieldName
_dfFunction :: ConsolidateFunction
_dfName :: Text
_dfField :: PivotFieldName
..} =
      Name -> [(Name, Text)] -> Element
leafElement Name
"dataField" ([(Name, Text)] -> Element) -> [(Name, Text)] -> Element
forall a b. (a -> b) -> a -> b
$
      [Maybe (Name, Text)] -> [(Name, Text)]
forall a. [Maybe a] -> [a]
catMaybes
        [ Name
"name" Name -> Maybe Text -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Text -> Maybe Text
forall a. a -> Maybe a
Just Text
_dfName
        , Name
"fld" Name -> Maybe Int -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Int -> Maybe Int
forall a. a -> Maybe a
Just (PivotFieldName -> Int
mapFieldToX PivotFieldName
_dfField)
        , Name
"subtotal" Name -> Maybe ConsolidateFunction -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? ConsolidateFunction
-> ConsolidateFunction -> Maybe ConsolidateFunction
forall a. Eq a => a -> a -> Maybe a
justNonDef ConsolidateFunction
ConsolidateSum ConsolidateFunction
_dfFunction
        ]

generateCache :: CellMap -> PivotTable -> CacheDefinition
generateCache :: CellMap -> PivotTable -> CacheDefinition
generateCache CellMap
cm PivotTable {Bool
[DataField]
[PositionedField]
[PivotFieldInfo]
Text
CellRef
_pvtSrcRef :: CellRef
_pvtSrcSheet :: Text
_pvtLocation :: CellRef
_pvtOutlineData :: Bool
_pvtOutline :: Bool
_pvtColumnGrandTotals :: Bool
_pvtRowGrandTotals :: Bool
_pvtFields :: [PivotFieldInfo]
_pvtDataFields :: [DataField]
_pvtColumnFields :: [PositionedField]
_pvtRowFields :: [PositionedField]
_pvtDataCaption :: Text
_pvtName :: Text
_pvtSrcRef :: PivotTable -> CellRef
_pvtSrcSheet :: PivotTable -> Text
_pvtLocation :: PivotTable -> CellRef
_pvtOutlineData :: PivotTable -> Bool
_pvtOutline :: PivotTable -> Bool
_pvtColumnGrandTotals :: PivotTable -> Bool
_pvtRowGrandTotals :: PivotTable -> Bool
_pvtFields :: PivotTable -> [PivotFieldInfo]
_pvtDataFields :: PivotTable -> [DataField]
_pvtColumnFields :: PivotTable -> [PositionedField]
_pvtRowFields :: PivotTable -> [PositionedField]
_pvtDataCaption :: PivotTable -> Text
_pvtName :: PivotTable -> Text
..} =
  CacheDefinition :: CellRef -> Text -> [CacheField] -> CacheDefinition
CacheDefinition
  { cdSourceRef :: CellRef
cdSourceRef = CellRef
_pvtSrcRef
  , cdSourceSheet :: Text
cdSourceSheet = Text
_pvtSrcSheet
  , cdFields :: [CacheField]
cdFields = [CacheField]
cachedFields
  }
  where
    cachedFields :: [CacheField]
cachedFields = (PivotFieldInfo -> Maybe CacheField)
-> [PivotFieldInfo] -> [CacheField]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ((PivotFieldName -> CacheField)
-> Maybe PivotFieldName -> Maybe CacheField
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap PivotFieldName -> CacheField
cache (Maybe PivotFieldName -> Maybe CacheField)
-> (PivotFieldInfo -> Maybe PivotFieldName)
-> PivotFieldInfo
-> Maybe CacheField
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PivotFieldInfo -> Maybe PivotFieldName
_pfiName) [PivotFieldInfo]
_pvtFields
    cache :: PivotFieldName -> CacheField
cache PivotFieldName
name =
      CacheField :: PivotFieldName -> [CellValue] -> CacheField
CacheField
      { cfName :: PivotFieldName
cfName = PivotFieldName
name
      , cfItems :: [CellValue]
cfItems =
          String -> Maybe [CellValue] -> [CellValue]
forall a. Partial => String -> Maybe a -> a
fromJustNote String
"specified pivot table field does not exist" (Maybe [CellValue] -> [CellValue])
-> Maybe [CellValue] -> [CellValue]
forall a b. (a -> b) -> a -> b
$
          PivotFieldName
-> Map PivotFieldName [CellValue] -> Maybe [CellValue]
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup PivotFieldName
name Map PivotFieldName [CellValue]
itemsByName
      }
    ((Int
r1, Int
c1), (Int
r2, Int
c2)) =
      String
-> Maybe ((Int, Int), (Int, Int)) -> ((Int, Int), (Int, Int))
forall a. Partial => String -> Maybe a -> a
fromJustNote String
"Invalid src ref of pivot table " (Maybe ((Int, Int), (Int, Int)) -> ((Int, Int), (Int, Int)))
-> Maybe ((Int, Int), (Int, Int)) -> ((Int, Int), (Int, Int))
forall a b. (a -> b) -> a -> b
$ CellRef -> Maybe ((Int, Int), (Int, Int))
fromRange CellRef
_pvtSrcRef
    getCellValue :: (Int, Int) -> Maybe CellValue
getCellValue (Int, Int)
ix = (Int, Int) -> CellMap -> Maybe Cell
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup (Int, Int)
ix CellMap
cm Maybe Cell -> (Cell -> Maybe CellValue) -> Maybe CellValue
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Cell -> Maybe CellValue
_cellValue
    itemsByName :: Map PivotFieldName [CellValue]
itemsByName =
      [(PivotFieldName, [CellValue])] -> Map PivotFieldName [CellValue]
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList ([(PivotFieldName, [CellValue])] -> Map PivotFieldName [CellValue])
-> [(PivotFieldName, [CellValue])]
-> Map PivotFieldName [CellValue]
forall a b. (a -> b) -> a -> b
$
      ((Int -> Maybe (PivotFieldName, [CellValue]))
 -> [Int] -> [(PivotFieldName, [CellValue])])
-> [Int]
-> (Int -> Maybe (PivotFieldName, [CellValue]))
-> [(PivotFieldName, [CellValue])]
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Int -> Maybe (PivotFieldName, [CellValue]))
-> [Int] -> [(PivotFieldName, [CellValue])]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe [Int
c1 .. Int
c2] ((Int -> Maybe (PivotFieldName, [CellValue]))
 -> [(PivotFieldName, [CellValue])])
-> (Int -> Maybe (PivotFieldName, [CellValue]))
-> [(PivotFieldName, [CellValue])]
forall a b. (a -> b) -> a -> b
$ \Int
c -> do
        CellText Text
nm <- (Int, Int) -> Maybe CellValue
getCellValue (Int
r1, Int
c)
        let values :: [CellValue]
values = (Int -> Maybe CellValue) -> [Int] -> [CellValue]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (\Int
r -> (Int, Int) -> Maybe CellValue
getCellValue (Int
r, Int
c)) [(Int
r1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) .. Int
r2]
        (PivotFieldName, [CellValue])
-> Maybe (PivotFieldName, [CellValue])
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> PivotFieldName
PivotFieldName Text
nm, [CellValue] -> [CellValue]
forall a. Ord a => [a] -> [a]
nubOrd [CellValue]
values)

writeCache :: CacheDefinition -> (Document, Document)
writeCache :: CacheDefinition -> (Document, Document)
writeCache CacheDefinition {[CacheField]
Text
CellRef
cdFields :: [CacheField]
cdSourceSheet :: Text
cdSourceRef :: CellRef
cdFields :: CacheDefinition -> [CacheField]
cdSourceSheet :: CacheDefinition -> Text
cdSourceRef :: CacheDefinition -> CellRef
..} = (Document
cacheDefDoc, Document
cacheRecordsDoc)
  where
    cacheDefDoc :: Document
cacheDefDoc =
      Text -> Element -> Document
documentFromElement Text
"Pivot cache definition generated by xlsx" (Element -> Document) -> Element -> Document
forall a b. (a -> b) -> a -> b
$
      Name -> [(Name, Text)] -> [Element] -> Element
elementList Name
"pivotCacheDefinition" [(Name, Text)]
attrs [Element]
elements
    attrs :: [(Name, Text)]
attrs = [Name
"invalid" Name -> Bool -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Bool
True, Name
"refreshOnLoad" Name -> Bool -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Bool
True, Text -> Name
odr Text
"id" Name -> RefId -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Int -> RefId
unsafeRefId Int
1]
    elements :: [Element]
elements = [Element
worksheetSource, Element
cacheFields]
    worksheetSource :: Element
worksheetSource =
      Name -> [(Name, Text)] -> [Element] -> Element
elementList
        Name
"cacheSource"
        [Name
"type" Name -> Text -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= (Text
"worksheet" :: Text)]
        [ Name -> [(Name, Text)] -> Element
leafElement
            Name
"worksheetSource"
            [Name
"ref" Name -> CellRef -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= CellRef
cdSourceRef, Name
"sheet" Name -> Text -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Text
cdSourceSheet]
        ]
    cacheFields :: Element
cacheFields =
      Name -> [Element] -> Element
elementListSimple Name
"cacheFields" ([Element] -> Element) -> [Element] -> Element
forall a b. (a -> b) -> a -> b
$ (CacheField -> Element) -> [CacheField] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
map (Name -> CacheField -> Element
forall a. ToElement a => Name -> a -> Element
toElement Name
"cacheField") [CacheField]
cdFields
    cacheRecordsDoc :: Document
cacheRecordsDoc =
      Text -> Element -> Document
documentFromElement Text
"Pivot cache records generated by xlsx" (Element -> Document)
-> ([Element] -> Element) -> [Element] -> Document
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
      Name -> [Element] -> Element
elementListSimple Name
"pivotCacheRecords" ([Element] -> Document) -> [Element] -> Document
forall a b. (a -> b) -> a -> b
$
      ([CacheRecordValue] -> Element)
-> [[CacheRecordValue]] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
map (Name -> [Element] -> Element
elementListSimple Name
"r" ([Element] -> Element)
-> ([CacheRecordValue] -> [Element])
-> [CacheRecordValue]
-> Element
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CacheRecordValue -> Element) -> [CacheRecordValue] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
map CacheRecordValue -> Element
recordValueToEl) [[CacheRecordValue]]
cacheRecords
    recordValueToEl :: CacheRecordValue -> Element
recordValueToEl (CacheText Text
t) = Name -> [(Name, Text)] -> Element
leafElement Name
"s" [Name
"v" Name -> Text -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Text
t]
    recordValueToEl (CacheNumber Double
n) = Name -> [(Name, Text)] -> Element
leafElement Name
"n" [Name
"v" Name -> Double -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Double
n]
    recordValueToEl (CacheIndex Int
i) = Name -> [(Name, Text)] -> Element
leafElement Name
"x" [Name
"v" Name -> Int -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Int
i]
    cacheRecords :: [[CacheRecordValue]]
cacheRecords = [[CacheRecordValue]] -> [[CacheRecordValue]]
forall a. [[a]] -> [[a]]
transpose ([[CacheRecordValue]] -> [[CacheRecordValue]])
-> [[CacheRecordValue]] -> [[CacheRecordValue]]
forall a b. (a -> b) -> a -> b
$ (CacheField -> [CacheRecordValue])
-> [CacheField] -> [[CacheRecordValue]]
forall a b. (a -> b) -> [a] -> [b]
map ([CellValue] -> [CacheRecordValue]
itemsToRecordValues ([CellValue] -> [CacheRecordValue])
-> (CacheField -> [CellValue]) -> CacheField -> [CacheRecordValue]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CacheField -> [CellValue]
cfItems) [CacheField]
cdFields
    itemsToRecordValues :: [CellValue] -> [CacheRecordValue]
itemsToRecordValues [CellValue]
vals =
      if (CellValue -> Bool) -> [CellValue] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all CellValue -> Bool
isText [CellValue]
vals
        then [CellValue] -> [CacheRecordValue]
forall a. Eq a => [a] -> [CacheRecordValue]
indexes [CellValue]
vals
        else (CellValue -> CacheRecordValue)
-> [CellValue] -> [CacheRecordValue]
forall a b. (a -> b) -> [a] -> [b]
map CellValue -> CacheRecordValue
itemToRecordValue [CellValue]
vals
    isText :: CellValue -> Bool
isText (CellText Text
_) = Bool
True
    isText CellValue
_ = Bool
False
    indexes :: [a] -> [CacheRecordValue]
indexes [a]
vals =
      [ Int -> CacheRecordValue
CacheIndex (Int -> CacheRecordValue)
-> (Maybe Int -> Int) -> Maybe Int -> CacheRecordValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe Int -> Int
forall a. Partial => String -> Maybe a -> a
fromJustNote String
"inconsistend definition" (Maybe Int -> CacheRecordValue) -> Maybe Int -> CacheRecordValue
forall a b. (a -> b) -> a -> b
$ a -> [a] -> Maybe Int
forall a. Eq a => a -> [a] -> Maybe Int
elemIndex a
v [a]
vals
      | a
v <- [a]
vals
      ]
    itemToRecordValue :: CellValue -> CacheRecordValue
itemToRecordValue (CellDouble Double
d) = Double -> CacheRecordValue
CacheNumber Double
d
    itemToRecordValue (CellText Text
t) = Text -> CacheRecordValue
CacheText Text
t
    itemToRecordValue CellValue
v = String -> CacheRecordValue
forall a. Partial => String -> a
error (String -> CacheRecordValue) -> String -> CacheRecordValue
forall a b. (a -> b) -> a -> b
$ String
"Unsupported value for pivot tables: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ CellValue -> String
forall a. Show a => a -> String
show CellValue
v