weekdaze-0.0.0.1: src-lib/WeekDaze/Aggregate/GroupCatalogue.hs
{-# LANGUAGE CPP, FlexibleContexts #-}
{-
Copyright (C) 2013-2015 Dr. Alistair Ward
This file is part of WeekDaze.
WeekDaze is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
WeekDaze is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with WeekDaze. If not, see <http://www.gnu.org/licenses/>.
-}
{- |
[@AUTHOR@] Dr. Alistair Ward
[@DESCRIPTION@] Defines the name & attributes, of all /group/s.
-}
module WeekDaze.Aggregate.GroupCatalogue(
-- * Types
-- ** Type-synonyms
GroupCatalogue,
ResourceIdsByGroupId,
-- * Constants
tag,
-- * Functions
extractDistinctMeetingLocationIds,
getMeetingTimes
#ifdef USE_HDBC
-- ** Translation
,fromDatabase
#endif
) where
import qualified Data.Map
import Data.Map((!))
import qualified Data.Maybe
import qualified Data.Set
import qualified WeekDaze.Data.Group as Data.Group
import qualified WeekDaze.Data.Location as Data.Location
import qualified WeekDaze.Data.Resource as Data.Resource
import qualified WeekDaze.Temporal.Time as Temporal.Time
#ifdef USE_HDBC
import qualified Database.HDBC
import qualified Data.Convertible
import qualified WeekDaze.Database.Selector as Database.Selector
import qualified WeekDaze.Temporal.Day as Temporal.Day
-- | Construct from the specified database-connection.
fromDatabase :: (
Database.HDBC.IConnection connection,
Data.Convertible.Convertible Database.HDBC.SqlValue locationId, -- Flexible context.
Data.Convertible.Convertible Database.HDBC.SqlValue timeslotId, -- Flexible context.
Ord timeslotId
)
=> connection -- ^ An abstract database-connection.
-> Database.HDBC.SqlValue -- ^ The project-id.
-> IO (GroupCatalogue timeslotId locationId)
fromDatabase connection projectIdSql = let
groupCatalogueIdColumnName :: Database.Selector.ColumnName
groupCatalogueIdColumnName = showString tag "Id";
groupCatalogueTableName, meetingTimesTableName :: Database.Selector.TableName
groupCatalogueTableName = showString Database.Selector.tablePrefix tag
meetingTimesTableName = showString Database.Selector.tablePrefix "meetingTime"
in do
#ifdef USE_HDBC_ODBC
selectMeetingTimesForGroupCatalogueId <- Database.Selector.prepare connection [Temporal.Day.tag, Database.Selector.timeslotIdColumnName] [meetingTimesTableName] [groupCatalogueIdColumnName]
#endif
Database.Selector.select connection [
groupCatalogueIdColumnName,
Data.Group.groupIdTag,
Database.Selector.locationIdColumnName,
Data.Group.mandatesAttendanceTag
] [groupCatalogueTableName] [(Database.Selector.projectIdColumnName, projectIdSql)] >>= fmap Data.Map.fromList . mapM (
\groupRow -> case groupRow of
[groupCatalogueIdSql, groupIdSql, locationIdSql, mandatesAttendanceSql] -> do
meetingTimes <- (
Data.Set.fromList . map (
\meetingTimesRow -> case meetingTimesRow of
[day, timeslotId] -> Temporal.Time.mkTimeFromSql day timeslotId
_ -> error . showString "WeekDaze.Aggregate.GroupCatalogue.fromDatabase:\tunexpected number of columns=" . shows (length meetingTimesRow) . showString " in row of table " . shows meetingTimesTableName . showString ", where " . showString Data.Group.groupIdTag . showChar '=' $ shows groupIdSql "."
)
#ifdef USE_HDBC_ODBC
) `fmap` (
Database.HDBC.execute selectMeetingTimesForGroupCatalogueId [groupCatalogueIdSql] >> Database.HDBC.fetchAllRows' selectMeetingTimesForGroupCatalogueId
)
#else
) `fmap` Database.Selector.select connection [
Temporal.Day.tag,
Database.Selector.timeslotIdColumnName
] [meetingTimesTableName] [(groupCatalogueIdColumnName, groupCatalogueIdSql)]
#endif
return {-to IO-monad-} (
Data.Maybe.fromMaybe (
error . showString "WeekDaze.Aggregate.GroupCatalogue.fromDatabase:\tnull " $ shows Data.Group.groupIdTag "."
) $ Database.HDBC.fromSql groupIdSql,
Data.Group.mkProfile meetingTimes (
Database.HDBC.fromSql locationIdSql -- Returns Nothing for SqlNull.
) $ Data.Maybe.fromMaybe Data.Group.defaultMandatesAttendance . either (
error . showString "WeekDaze.Aggregate.GroupCatalogue.fromDatabase:\tfailed to parse the value for " . shows Data.Group.mandatesAttendanceTag . showString " read from the database; " . show
) id $ Database.HDBC.safeFromSql mandatesAttendanceSql
) -- Pair.
_ -> error . showString "WeekDaze.Aggregate.GroupCatalogue.fromDatabase:\tunexpected number of columns=" . shows (length groupRow) . showString " in row of table " $ shows groupCatalogueTableName "."
)
#endif /* USE_HDBC */
-- | Used to qualify XML.
tag :: String
tag = "groupCatalogue"
-- | The complete set of 'Data.Group.Profile', indexed by 'Data.Group.Id'.
type GroupCatalogue timeslotId locationId = Data.Resource.ResourceMap Data.Group.Id (Data.Group.Profile timeslotId locationId)
-- | A map indexed by a /group/-id, of /resource-id/s.
type ResourceIdsByGroupId resourceId = Data.Map.Map Data.Group.Id (Data.Set.Set resourceId)
-- | Extracts the set of distinct 'locationId's, from the /group-catalogue/.
extractDistinctMeetingLocationIds :: Ord locationId => GroupCatalogue timeslotId locationId -> Data.Location.Locus locationId
extractDistinctMeetingLocationIds = Data.Map.foldr (\profile locus -> Data.Maybe.maybe locus (`Data.Set.insert` locus) $ Data.Group.getMaybeLocationId profile) Data.Set.empty
-- | Gather the set of /meeting-time/s corresponding to the specified set of /group/s.
getMeetingTimes :: Ord timeslotId => GroupCatalogue timeslotId locationId -> Data.Group.Membership -> Temporal.Time.TimeSet timeslotId
getMeetingTimes groupCatalogue = Data.Set.foldr (Data.Set.union . Data.Group.getMeetingTimes . (groupCatalogue !)) Data.Set.empty