packages feed

aws-academy-grade-exporter (empty) → 0.1.0.0

raw patch · 8 files changed

+287/−0 lines, 8 filesdep +aesondep +basedep +bytestring

Dependencies added: aeson, base, bytestring, cassava, optparse-applicative, postgresql-simple, req, text, vector

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for aws-academy-grade-exporter++## 0.1.0.0 -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2025 Alexander Goussas++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ aws-academy-grade-exporter.cabal view
@@ -0,0 +1,56 @@+cabal-version:   3.0+name:            aws-academy-grade-exporter+version:         0.1.0.0+synopsis:        Export grades from AWS Academy to different formats+description:+  A CLI tool for exporting grades from AWS Academy courses to various formats and sinks.++homepage:        https://github.com/aloussase/aws-academy-grade-exporter-hs+license:         MIT+license-file:    LICENSE+author:          Alexander Goussas+maintainer:      goussasalexander@gmail.com++-- copyright:+category:        Text+build-type:      Simple+extra-doc-files: CHANGELOG.md++-- extra-source-files:++source-repository head+  type:     git+  location:+    https://github.com/disel-espol/aws-academy-grades-exporter-hs++common warnings+  ghc-options: -Wall++executable aws-academy-grade-exporter+  import:             warnings+  main-is:            Main.hs+  other-modules:+    Exporter+    Options+    Pg+    Row++  -- other-extensions:+  build-depends:+    , aeson                 >=2.2.3     && <2.3+    , base                  ^>=4.17.2.1+    , bytestring            >=0.11.5    && <0.12+    , cassava               >=0.5.3     && <0.6+    , optparse-applicative  >=0.18.1    && <0.19+    , postgresql-simple     >=0.7.0     && <0.8+    , req                   >=3.13.4    && <3.14+    , text                  >=2.0.2     && <2.1+    , vector                >=0.13.2    && <0.14++  hs-source-dirs:     src+  default-language:   Haskell2010+  default-extensions:+    DeriveAnyClass+    DeriveGeneric+    DerivingStrategies+    OverloadedStrings
+ src/Exporter.hs view
@@ -0,0 +1,21 @@+module Exporter where++import           Control.Exception+import qualified Pg+import           Row++data ExporterHandle = ExporterHandle+  { ehExport :: [Row] -> IO ()+  }+++stdoutExporter :: ExporterHandle+stdoutExporter = ExporterHandle print++postgresExporter :: ExporterHandle+postgresExporter = ExporterHandle $ \rows -> do+  bracket+    (Pg.connect)+    (Pg.disconnect)+    (\conn -> Pg.insertRows conn rows)+
+ src/Main.hs view
@@ -0,0 +1,26 @@+module Main where++import qualified Data.ByteString.Lazy.Char8 as BL8+import           Data.Csv+import qualified Data.Vector                as V+import           Exporter+import qualified Options+import           Row+import           System.IO                  (readFile')++readRows :: FilePath -> IO [Row]+readRows fileName = do+  contents <- readFile' fileName+  case decode HasHeader $ BL8.pack contents of+    Left err -> error err+    Right v  -> return $ V.toList v++doExport :: Options.Dest -> [Row] -> IO ()+doExport Options.Postgres rows = Exporter.ehExport postgresExporter $ rows+doExport Options.Stdout rows   = Exporter.ehExport stdoutExporter $ rows++main :: IO ()+main = do+  opts <- Options.parseOpts+  rows <- readRows (Options.optFile opts)+  doExport (Options.optDest opts) rows
+ src/Options.hs view
@@ -0,0 +1,42 @@+module Options (parseOpts, Opt (..), Dest (..)) where++import           Options.Applicative++data Dest = Postgres | Stdout+  deriving Show++data Opt = MkOpt+  { optDest :: Dest+  , optFile :: FilePath+  }+  deriving Show++opt :: Parser Opt+opt = MkOpt+  <$> option readDest+      ( long "destination"+      <> short 'd'+      <> help "Where to export the data"+      )+  <*> strOption+      ( long "file"+      <> short 'f'+      <> help "The file to be processed"+      )++  where+    readDest = eitherReader $ \s ->+      case s of+        "postgres" -> pure Postgres+        "stdout"   -> pure Stdout+        _          -> Left "Invalid exporter"++parseOpts :: IO Opt+parseOpts = execParser opts++opts :: ParserInfo Opt+opts = info (opt <**> helper)+      ( fullDesc+      <> progDesc "Export grade from AWS Academy to various different formats"+      <> header "aws-academy-grade-exporter - a tool for exporting grades from AWS Academy to various formats"+      )
+ src/Pg.hs view
@@ -0,0 +1,57 @@+module Pg where++import           Control.Monad              (forM, void)+import           Data.Aeson                 (FromJSON)+import qualified Data.ByteString.Char8      as BL8+import           Data.List                  (isSuffixOf)+import qualified Data.Text                  as T+import           Database.PostgreSQL.Simple+import           GHC.Generics               (Generic)+import           Network.HTTP.Req+import           Row+import           System.Environment++connect :: IO Connection+connect = do+  connStr <- getEnv "DB_URL"+  conn <- connectPostgreSQL $ BL8.pack connStr+  return conn++data StudentData = MkStudentData+  { nombres   :: !String+  , apellidos :: !String+  }+  deriving stock (Show, Generic)+  deriving anyclass (FromJSON)++insertRows :: Connection -> [Row] -> IO  ()+insertRows conn rows = do+  void $ executeMany+    conn+    "insert into grades values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"+    rows++  students <- forM rows $ \row -> do+    -- TODO: Should do this in parallel.+    let studentEmail = getStudentEmail row++    runReq defaultHttpConfig $ do+      r <- req+        GET+        (https "wsarchivos.espol.edu.ec" /: "api" /: "consultas" /: "persona" /: (T.pack studentEmail))+        NoReqBody+        jsonResponse+        mempty++      return $ head $ map (\sd -> (studentEmail, nombres sd, apellidos sd)) (responseBody r :: [StudentData])++  void $ executeMany conn "insert into students values (?,?,?)" (students :: [(String, String, String)])++getStudentEmail :: Row -> String+getStudentEmail row+  | not (null $ rLoginID row) = rLoginID row+  | "@espol.edu.ec" `isSuffixOf` rStudent row = rStudent row+  | otherwise = rStudent row ++ "@espol.edu.ec"++disconnect :: Connection -> IO ()+disconnect conn = close conn
+ src/Row.hs view
@@ -0,0 +1,60 @@+module Row where+++import           Data.Csv+import           Database.PostgreSQL.Simple.ToRow+import           GHC.Generics                     (Generic)++data Row = MkRow+  { rStudent                         :: !String+  , rId                              :: !Int+  , rLoginID                         :: !String+  , rSection                         :: !String+  , rModule1KC                       :: !(Maybe Double)+  , rModule2KC                       :: !(Maybe Double)+  , rModule3KC                       :: !(Maybe Double)+  , rModule4KC                       :: !(Maybe Double)+  , rModule5KC                       :: !(Maybe Double)+  , rModule6KC                       :: !(Maybe Double)+  , rModule7KC                       :: !(Maybe Double)+  , rModule8KC                       :: !(Maybe Double)+  , rModule9KC                       :: !(Maybe Double)+  , rModule10KC                      :: !(Maybe Double)+  , rCourseAssesment                 :: !(Maybe Double)+  , rLab1                            :: !(Maybe Double)+  , rLab2                            :: !(Maybe Double)+  , rLab3                            :: !(Maybe Double)+  , rActivityAwsLambda               :: !(Maybe Double)+  , rActivityElasticBeanstalk        :: !(Maybe Double)+  , rLab4                            :: !(Maybe Double)+  , rLab5                            :: !(Maybe Double)+  , rLab6                            :: !(Maybe Double)+  , rAssignmentsCurrentPoints        :: !(Maybe Double)+  , rAssignmentsFinalPoints          :: !(Maybe Double)+  , rAssignmentsCurrentScore         :: !(Maybe Double)+  , rAssignmentsUnpostedCurrentScore :: !(Maybe Double)+  , rAssignmentsFinalScore           :: !(Maybe Double)+  , rAssignmentsUnpostedFinalScore   :: !(Maybe Double)+  , rKCCurrentPoints                 :: !(Maybe Double)+  , rKCFinalPoints                   :: !(Maybe Double)+  , rKCCurrentScore                  :: !(Maybe Double)+  , rKCUnpostedCurrentScore          :: !(Maybe Double)+  , rKCFinalScore                    :: !(Maybe Double)+  , rKCUnpostedFinalScore            :: !(Maybe Double)+  , rLabsCurrentPoints               :: !(Maybe Double)+  , rLabsFinalPoints                 :: !(Maybe Double)+  , rLabsCurrentScore                :: !(Maybe Double)+  , rLabsUnpostedCurrentScore        :: !(Maybe Double)+  , rLabsFinalScore                  :: !(Maybe Double)+  , rLabsUnpostedFinalScore          :: !(Maybe Double)+  , rCurrentPoints                   :: !(Maybe Double)+  , rFinalPoints                     :: !(Maybe Double)+  , rCurrentScore                    :: !(Maybe Double)+  , rUnpostedCurrentScore            :: !(Maybe Double)+  , rFinalScore                      :: !(Maybe Double)+  , rUnpostedFinalScore              :: !(Maybe Double)+  }+  deriving stock (Show, Generic)+  deriving anyclass (ToRow)++instance FromRecord Row