Add project file
This commit is contained in:
parent
6e17cd75ab
commit
e2003440e8
157
Proj.hs
Normal file
157
Proj.hs
Normal file
@ -0,0 +1,157 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
import Control.Exception
|
||||
import Control.Monad
|
||||
import Data.ByteString.Lazy qualified as BL
|
||||
import Data.ByteString.Lazy.Char8 qualified as BLC
|
||||
import Data.Csv
|
||||
import Data.Function (on)
|
||||
import Data.List (maximumBy)
|
||||
import Data.Vector (Vector)
|
||||
import Data.Vector qualified as V
|
||||
import System.IO.Error (isDoesNotExistError)
|
||||
import System.Random (randomRIO)
|
||||
import Test.HUnit
|
||||
|
||||
-- Definicja typu Dish (Danie)
|
||||
data Dish = Dish
|
||||
{ dishName :: BLC.ByteString,
|
||||
recipe :: BLC.ByteString,
|
||||
calories :: Int
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
-- Definicja instancji Eq dla Dish
|
||||
instance Eq Dish where
|
||||
(Dish name1 _ cal1) == (Dish name2 _ cal2) = name1 == name2 && cal1 == cal2
|
||||
|
||||
-- Definicja instancji FromNamedRecord dla Dish do parsowania z CSV
|
||||
instance FromNamedRecord Dish where
|
||||
parseNamedRecord r =
|
||||
Dish
|
||||
<$> r .: "mealName"
|
||||
<*> r .: "mealRecipe"
|
||||
<*> r .: "mealCalories"
|
||||
|
||||
--instance FromNamedRecord Dish where
|
||||
--parseNamedRecord r = do
|
||||
--mealName <- r .: "mealName"
|
||||
--mealRecipe <- r .: "mealRecipe"
|
||||
--mealCalories <- r .: "mealCalories"
|
||||
--return (Dish mealName mealRecipe mealCalories)
|
||||
|
||||
|
||||
-- Definicja instancji ToNamedRecord dla Dish do zapisywania do CSV
|
||||
instance ToNamedRecord Dish where
|
||||
toNamedRecord (Dish name recipe cal) =
|
||||
namedRecord ["mealName" .= name, "mealRecipe" .= recipe, "mealCalories" .= cal]
|
||||
|
||||
-- Definicja instancji DefaultOrdered dla Dish do zachowania kolejności nagłówków w CSV
|
||||
instance DefaultOrdered Dish where
|
||||
headerOrder _ = header ["mealName", "mealRecipe", "mealCalories"]
|
||||
|
||||
-- Wczytuje plik CSV i parsuje go na listę dań
|
||||
readCSV :: FilePath -> IO [Dish]
|
||||
readCSV path = do
|
||||
contents <- BLC.readFile path
|
||||
case decodeByName contents of
|
||||
Left err -> do
|
||||
putStrLn $ "Błąd parsowania CSV: " ++ err
|
||||
return []
|
||||
Right (_, v) -> return $ V.toList v
|
||||
|
||||
-- Zapisuje listę dań do pliku CSV
|
||||
writeCSV :: FilePath -> [Dish] -> IO ()
|
||||
writeCSV path dishes = do
|
||||
let encoded = encodeDefaultOrderedByName dishes
|
||||
BL.writeFile path encoded
|
||||
|
||||
-- Proponuje posiłki na podstawie podanej ilości kalorii
|
||||
suggestMeals :: Int -> [Dish] -> IO (Dish, Dish, Dish)
|
||||
suggestMeals _ [] = return (Dish "" "" 0, Dish "" "" 0, Dish "" "" 0)
|
||||
suggestMeals targetCalories dishes = do
|
||||
breakfast <- chooseRandomMeal $ filter (\d -> calories d <= targetCalories `div` 3) dishes
|
||||
let remaining1 = filter (/= breakfast) dishes
|
||||
let lunch = chooseBestMeal (targetCalories - calories breakfast) $ filter (\d -> calories d <= targetCalories `div` 2) remaining1
|
||||
let remaining2 = filter (/= lunch) remaining1
|
||||
let dinner = chooseBestMeal (targetCalories - calories breakfast - calories lunch) remaining2
|
||||
return (breakfast, lunch, dinner)
|
||||
|
||||
-- Wybiera najlepsze danie na podstawie kalorii
|
||||
chooseBestMeal :: Int -> [Dish] -> Dish
|
||||
chooseBestMeal _ [] = Dish "" "" 0
|
||||
chooseBestMeal targetCalories meals = maximumBy (compare `on` calories) $ filter (\d -> calories d <= targetCalories) meals
|
||||
|
||||
-- Wybiera losowe danie z listy
|
||||
chooseRandomMeal :: [Dish] -> IO Dish
|
||||
chooseRandomMeal [] = return $ Dish "" "" 0
|
||||
chooseRandomMeal meals = do
|
||||
idx <- randomRIO (0, length meals - 1)
|
||||
return $ meals !! idx
|
||||
|
||||
-- Funkcja do wprowadzania nowych dań przez użytkownika
|
||||
addNewDishes :: FilePath -> IO ()
|
||||
addNewDishes path = do
|
||||
putStrLn "Ile dań chcesz dodać?"
|
||||
n <- readLn
|
||||
newDishes <- forM [1 .. n] $ \_ -> do
|
||||
putStrLn "Podaj nazwę dania:"
|
||||
name <- BLC.pack <$> getLine
|
||||
putStrLn "Podaj przepis:"
|
||||
recipe <- BLC.pack <$> getLine
|
||||
putStrLn "Podaj ilość kalorii:"
|
||||
cal <- readLn
|
||||
return $ Dish name recipe cal
|
||||
existingDishes <- readCSV path
|
||||
let allDishes = existingDishes ++ newDishes
|
||||
writeCSV path allDishes
|
||||
putStrLn "Nowe dania zostały zapisane do pliku."
|
||||
|
||||
-- Testy jednostkowe
|
||||
testSuggestMeals :: Test
|
||||
testSuggestMeals =
|
||||
TestList
|
||||
[ "Test suggestMeals for empty dish list"
|
||||
~: do
|
||||
let csvFile = "empty.csv"
|
||||
result <- tryJust (guard . isDoesNotExistError) $ readCSV csvFile
|
||||
case result of
|
||||
Left _ -> return () -- Test zakończy się powodzeniem, jeśli otrzymamy Left
|
||||
Right _ -> assertFailure ("Test suggestMeals for empty dish list with file " ++ csvFile ++ ": should have failed to read CSV file"),
|
||||
"Test suggestMeals for non-empty dish list"
|
||||
~: do
|
||||
let csvFile = "baza.csv"
|
||||
targetCalories = 1400
|
||||
result <- tryJust (guard . isDoesNotExistError) $ readCSV csvFile
|
||||
case result of
|
||||
Left _ -> assertFailure ("Test suggestMeals for non-empty dish list with file " ++ csvFile ++ ": could not read CSV file")
|
||||
Right dishes -> do
|
||||
(breakfast, lunch, dinner) <- suggestMeals targetCalories dishes
|
||||
assertBool "Śniadanie nie powinno być puste" (dishName breakfast /= "" && calories breakfast > 0)
|
||||
assertBool "Obiad nie powinien być pusty" (dishName lunch /= "" && calories lunch > 0)
|
||||
assertBool "Kolacja nie powinna być pusta" (dishName dinner /= "" && calories dinner > 0)
|
||||
]
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
testResult <- runTestTT testSuggestMeals
|
||||
putStrLn "Co chcesz zrobić? (1) Otrzymać menu na pewną ilość kalorii (2) Dodać nowe dania"
|
||||
choice <- getLine
|
||||
let csvFile = "baza.csv"
|
||||
case choice of
|
||||
"1" -> do
|
||||
putStrLn "Podaj średnią ilość kalorii na dzień:"
|
||||
targetCalories <- readLn
|
||||
result <- tryJust (guard . isDoesNotExistError) $ readCSV csvFile
|
||||
case result of
|
||||
Left _ -> putStrLn $ "Plik " ++ csvFile ++ " nie istnieje."
|
||||
Right dishes -> do
|
||||
(breakfast, lunch, dinner) <- suggestMeals targetCalories dishes
|
||||
putStrLn "Śniadanie:"
|
||||
print breakfast
|
||||
putStrLn "Obiad:"
|
||||
print lunch
|
||||
putStrLn "Kolacja:"
|
||||
print dinner
|
||||
"2" -> addNewDishes csvFile
|
||||
_ -> putStrLn "Niepoprawny wybór."
|
Loading…
Reference in New Issue
Block a user