هک کردن واتسون با Haskell – قسمت 2
در پست قبلی وبلاگ، فریم های Watson را از یک فایل JSON خواندیم. در این پست وبلاگ، فایل حالت Watson را می خوانیم و آن را در خروجی استاندارد چاپ می کنیم.
ایالت واتسون
علاوه بر فریم ها، واتسون وضعیت فریم را نیز در یک فایل JSON جداگانه به نام ذخیره می کند state
. تا جایی که من متوجه شدم، در یک زمان معین 3 حالت ممکن وجود دارد:
- هیچ فایل حالتی وجود ندارد: شما Watson را نصب کرده اید، اما هنوز زمان ردیابی را شروع نکرده اید.
- فایل حالت خالی است: شما شروع به استفاده از Watson کرده اید، اما در حال حاضر هیچ زمانی را ردیابی نمی کنید.
- فایل وضعیت حاوی برخی از داده ها است: شما در حال ردیابی زمان هستید.
وقتی می گوییم که فایل حالت خالی است، در واقع یک شی JSON خالی است:
{}
وقتی فایل حالت حاوی مقداری داده باشد، یک شی JSON با 3 کلید است. project
، start
زمان و tags
لیست:
{
"project": "project1",
"start": 1629043200,
"tags": ["tag1", "tag2"]
}
بیایید با این فایل حالت کار کنیم…
برنامه
این پست وبلاگ یک برنامه Literate Haskell است که سعی می کند حالت Watson را از یک فایل JSON بخواند و آن را در خروجی استاندارد چاپ کند. برای همین پست وبلاگ تمام شد.
بیایید با پسوندهای زبان شروع کنیم:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
ما از پکیج aeson مانند پست قبلی، علاوه بر کتابخانه هایی که با GHC عرضه می شوند، استفاده خواهیم کرد. بیایید واردات خود را اجرا کنیم:
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
import System.Directory (doesFileExist)
import System.Environment (getArgs)
import qualified Data.Aeson as Aeson
import qualified Data.Text as T
import qualified Data.Time as Time
تابع نقطه ورودی ما است main
طبق معمول در اینجا چیزی است که انجام خواهد داد:
- مسیر فایل JSON حالت را از آرگومان های خط فرمان بخوانید.
- سعی کنید وضعیت را از فایل بخوانید.
- در صورت موفقیت آمیز خواندن، وضعیت فعلی را در خروجی استاندارد چاپ کنید، در غیر این صورت یک پیام مناسب چاپ کنید.
main :: IO ()
main = do
ابتدا سعی کنید مسیر را از روی آرگومان های خط فرمان بخوانید:
fp <- head <$> getArgs
سپس سعی کنید وضعیت را از فایل بخوانید:
mState <- readState fp
در حال حاضر، ما یک نتیجه از نوع Maybe CurrentState
. ما الگوی مطابقت را برای چاپ وضعیت در صورت موجود بودن، انجام می دهیم:
case mState of
Just state -> print state
Nothing -> putStrLn "State file can not be parsed."
همه چیز همین است main
عملکرد انجام می دهد. حالا بیایید آن را تعریف کنیم CurrentState
نوع داده که a است مجموع کدگذاری هر دو حالت بدون ردیابی و ردیابی را تایپ کنید:
data CurrentState
= CurrentStatePending
| CurrentStateRunning
{ currentStateRunningSince :: !Time.UTCTime
, currentStateRunningProject :: !T.Text
, currentStateRunningTags :: ![T.Text]
}
… و اضافه کنید Show
و Eq
نمونه هایی برای آن:
deriving (Show, Eq)
بیایید یک نمونه از آن را تعریف کنیم FromJSON
کلاس برای آن تایپ کنید. ما با یک شی JSON مطابقت خواهیم داشت:
instance Aeson.FromJSON CurrentState where
parseJSON = Aeson.withObject "CurrentState" $ \o -> do
اگر شی خالی باشد، برمی گردیم CurrentStatePending
:
if null o
then pure CurrentStatePending
…، در غیر این صورت سعی کنید آن را تجزیه کنید project
، start
و tags
زمینه ها:
else CurrentStateRunning
<$> (fromEpoch <$> o Aeson..: "start")
<*> o Aeson..: "project"
<*> o Aeson..: "tags"
… جایی که ما fromEpoch
تابع یک زمان دوره ای را به a تبدیل می کند UTCTime
ارزش:
where
fromEpoch = posixSecondsToUTCTime . fromIntegral @Int
حال میتوانیم آن را تعریف کنیم readState
تابع:
readState :: FilePath -> IO (Maybe CurrentState)
readState fp = do
exists <- doesFileExist fp
if exists
then do
mState <- Aeson.eitherDecodeFileStrict fp
pure $ case mState of
Left _ -> Nothing
Right state -> Just state
else pure $ Just CurrentStatePending
انجام شد!
طبق معمول، بیایید یک پیوند نمادین به فایل کد منبع ایجاد کنیم:
ln -sr \
content/posts/2024-08-16_hacking-watson-part-2.md \
content/posts/2024-08-16_hacking-watson-part-2.lhs
…، برنامه ما را اجرا کنید:
runhaskell -pgmLmarkdown-unlit content/posts/2024-08-16_hacking-watson-part-2.lhs ~/.config/watson/state
…، و خروجی را ببینید:
CurrentStateRunning {currentStateRunningSince = 2024-08-16 14:53:57 UTC, currentStateRunningProject = "vst", currentStateRunningTags = ["gh:vst/vst.github.io"]}
جمع بندی
با این پست وبلاگ، ما آن را تکمیل کردیم خواندن بخشی از فایل های واتسون در پست بعدی وبلاگ، فایل وضعیت را می نویسیم و به جای Watson CLI با Haskell زمان را ردیابی می کنیم.