برنامه نویسی

هک کردن واتسون با Haskell – قسمت 2

در پست قبلی وبلاگ، فریم های Watson را از یک فایل JSON خواندیم. در این پست وبلاگ، فایل حالت Watson را می خوانیم و آن را در خروجی استاندارد چاپ می کنیم.

ایالت واتسون

علاوه بر فریم ها، واتسون وضعیت فریم را نیز در یک فایل JSON جداگانه به نام ذخیره می کند state. تا جایی که من متوجه شدم، در یک زمان معین 3 حالت ممکن وجود دارد:

  1. هیچ فایل حالتی وجود ندارد: شما Watson را نصب کرده اید، اما هنوز زمان ردیابی را شروع نکرده اید.
  2. فایل حالت خالی است: شما شروع به استفاده از Watson کرده اید، اما در حال حاضر هیچ زمانی را ردیابی نمی کنید.
  3. فایل وضعیت حاوی برخی از داده ها است: شما در حال ردیابی زمان هستید.

وقتی می گوییم که فایل حالت خالی است، در واقع یک شی 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 طبق معمول در اینجا چیزی است که انجام خواهد داد:

  1. مسیر فایل JSON حالت را از آرگومان های خط فرمان بخوانید.
  2. سعی کنید وضعیت را از فایل بخوانید.
  3. در صورت موفقیت آمیز خواندن، وضعیت فعلی را در خروجی استاندارد چاپ کنید، در غیر این صورت یک پیام مناسب چاپ کنید.
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 زمان را ردیابی می کنیم.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا