packages feed

dataframe-persistent 0.1.0.0 → 0.2.0.0

raw patch · 5 files changed

+43/−27 lines, 5 filesdep ~dataframePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: dataframe

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -0,0 +1,5 @@+## 0.2.0.0+* Support generating dataframe expressions for a table so we don't have to type them out manually. Works the same as `deriveColumns`.++## 0.1.0.0+* Initial version supporting reading dataframes from SQL using Persistent.
README.md view
@@ -17,7 +17,7 @@  ```yaml dependencies:-- dataframe ^>= 0.3+- dataframe ^>= 0.4 - dataframe-persistent ^>= 0.1 - persistent >= 2.14 - persistent-sqlite >= 2.13  # or your preferred backend@@ -27,7 +27,7 @@  ```cabal build-depends:-  dataframe ^>= 0.3,+  dataframe ^>= 0.4,   dataframe-persistent ^>= 0.1,   persistent >= 2.14,   persistent-sqlite >= 2.13@@ -60,6 +60,8 @@ import DataFrame.IO.Persistent.TH import qualified Data.Vector as V +import DataFrame.Functions ((.<))+ -- Define your entities share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| TestUser@@ -92,8 +94,9 @@     liftIO $ putStrLn $ "Active users: " ++ show (nRows activeUsersDF)          -- Process with DataFrame operations-    let youngUsers = DF.filter @Int "age" (< 30) allUsersDF-        ages = V.toList $ DF.columnAsVector @Int "age" youngUsers+    -- Expressions are automaticaly generated.+    let youngUsers = DF.filterWhere (test_user_age .< 30) allUsersDF+        ages = V.toList $ DF.columnAsVector test_user_age youngUsers     liftIO $ putStrLn $ "Young user ages: " ++ show ages          -- Custom configuration@@ -111,6 +114,7 @@ - **Template Haskell support** for automatic instance generation - **Configurable loading** with batch size and column selection - **Column name cleaning** - removes table prefixes automatically (e.g., `test_user_name` → `name`)+- **Automatically generate typed expressions** - creates expressions in snake case prefixed by table name (e.g `test_user_name`). - **Type preservation** - maintains proper types for Text, Int, Bool, Day, etc. - **Empty DataFrame support** - preserves column structure even with no data - **Support for all Persistent backends** (SQLite, PostgreSQL, MySQL, etc.)@@ -141,9 +145,9 @@  ```haskell -- Extract specific column data-let names = V.toList $ DF.columnAsVector @Text "name" df-    ages = V.toList $ DF.columnAsVector @Int "age" df-    activeFlags = V.toList $ DF.columnAsVector @Bool "active" df+let names = V.toList $ DF.columnAsVector test_user_name df+    ages = V.toList $ DF.columnAsVector test_user_age df+    activeFlags = V.toList $ DF.columnAsVector test_user_active df ```  ## Examples
dataframe-persistent.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               dataframe-persistent-version:            0.1.0.0+version:            0.2.0.0  synopsis: Persistent database integration for the dataframe library @@ -37,7 +37,7 @@     build-depends:    base >= 4 && <5,                       bytestring >= 0.11 && < 0.13,                       containers >= 0.6.7 && < 0.9,-                      dataframe ^>= 0.3,+                      dataframe ^>= 0.4,                       persistent >= 2.14 && < 3,                       template-haskell >= 2.0 && < 3,                       text >= 2.0 && < 3,@@ -53,20 +53,17 @@     main-is: Main.hs     other-modules: PersistentTests     build-depends:  base >= 4 && < 5,-                    dataframe ^>= 0.3,+                    dataframe ^>= 0.4,                     dataframe-persistent,-                    -- directory >= 1.3.0.0 && < 2,                     HUnit ^>= 1.6,                     monad-logger >= 0.3 && < 0.4,                     persistent >= 2.14 && < 3,                     persistent-sqlite >= 2.13 && < 3,-                    -- persistent-template >= 2.12 && < 3,                     resourcet >= 0.1 && < 2,                     temporary >= 1.3 && < 2,                     text >= 2.0 && < 3,                     time >= 1.12 && < 2,                     transformers >= 0.5 && < 0.7,-                    vector ^>= 0.13,-                    -- unliftio-core >= 0.2 && < 0.3+                    vector ^>= 0.13     hs-source-dirs: tests     default-language: Haskell2010
src/DataFrame/IO/Persistent/TH.hs view
@@ -19,14 +19,18 @@ import Data.Text (Text) import qualified Data.Text as T import qualified Data.Vector as V+import DataFrame.Functions (col) import DataFrame.IO.Persistent import qualified DataFrame.Internal.Column as DFCol+import DataFrame.Internal.Expression import Database.Persist import Database.Persist.Sql (fromSqlKey) import Database.Persist.TH import Language.Haskell.TH import Language.Haskell.TH.Syntax (Lift, lift) +import Debug.Trace (trace)+ -- | Derive EntityToDataFrame instance using Template Haskell deriveEntityToDataFrame :: Name -> Q [Dec] deriveEntityToDataFrame entityName = do@@ -64,8 +68,16 @@                         []                         (AppT (ConT ''EntityToDataFrame) (ConT entityName))                         [colNamesMethod, entityDataMethod]+            dataframeExprs <- forM fields $ \(raw, _, ty) -> do+                let nm = camelToSnake (nameBase raw)+                let colName = camelToSnake (drop (length entityNameStr) (nameBase raw))+                trace ((nm <> " :: Expr " <> (show ty))) pure ()+                let n = mkName nm+                sig <- sigD n [t|Expr $(pure ty)|]+                val <- valD (varP n) (normalB [|col $(lift colName)|]) []+                pure [sig, val] -            return [instanceDec]+            return ([instanceDec] ++ concat dataframeExprs)         _ ->             fail $                 "deriveEntityToDataFrame: " ++ show entityName ++ " must be a record type"
tests/PersistentTests.hs view
@@ -36,6 +36,8 @@ import Test.HUnit import UnliftIO.Resource (ResourceT) +import DataFrame.Functions ((.<))+ -- Define test entities share     [mkPersist sqlSettings, mkMigrate "migrateAll"]@@ -107,7 +109,7 @@         assertBool "Has active column" ("active" `elem` cols)          -- Check data values-        let names = V.toList (DF.columnAsVector @Text "name" df)+        let names = V.toList (DF.columnAsVector test_user_name df)          assertEqual "Names match" ["Alice", "Bob", "Charlie", "Diana"] names @@ -123,7 +125,7 @@             print cols             assertBool "Has id column" ("id" `elem` cols)             assertBool "Has name column" ("name" `elem` cols)-            let names = V.toList (DF.columnAsVector @Text "name" (DF.take 5 df))+            let names = V.toList (DF.columnAsVector artist_name (DF.take 5 df))              assertEqual                 "Names match"@@ -142,7 +144,7 @@         assertEqual "Active users count" 3 (nRows df)          -- Check all loaded users are active-        let activeFlags = V.toList $ DF.columnAsVector @Bool "active" df+        let activeFlags = V.toList $ DF.columnAsVector test_user_active df         assertBool "All active" (and activeFlags)  -- Test custom configuration@@ -190,23 +192,19 @@     df <- fromPersistent @TestUser []     liftIO $ do         -- Filter operation-        let youngUsers = DF.filter @Int "age" (< 30) df+        let youngUsers = DF.filter test_user_age (< 30) df         assertEqual "Young users count" 2 (nRows youngUsers)          -- Sort operation-        let sorted = DF.sortBy DF.Ascending ["age"] df-        let ages = V.toList $ DF.columnAsVector @Int "age" sorted+        let sorted = DF.sortBy [DF.Asc "age"] df+        let ages = V.toList $ DF.columnAsVector test_user_age sorted         assertEqual "Ages sorted" [25, 28, 30, 35] ages          -- Derive column         let withAgeGroup =-                DF.derive+                DF.derive @Text                     "age_group"-                    ( F.ifThenElse-                        (F.col @Int "age" `F.lt` F.lit 30)-                        (F.lit @Text "young")-                        (F.lit @Text "adult")-                    )+                    (F.ifThenElse (test_user_age .< 30) "young" "adult")                     df         assertEqual "Has age_group column" 5 (length $ DF.columnNames withAgeGroup)