(****************************************************************************** ** Copyright (c) 2004 ** ** This program 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 2 ** of the License, or (at your option) any later version. ** ** This program 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. ** ** To get a copy of the GNU General Public License, write to the ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, ** Boston, MA 02111-1307, USA. ** ** File Name : checkFcst.ml ** Author : ** E-mail : little@cs.utah.edu ** Date Created : 02/12/2004 ** ** Description : ** ** Assumptions : ** ** ToDo : ** ******************************************************************************* ** CVS HOOKS ** ** $Source: /home/little/srl_cvs/school/hw/cs6520/ocaml/checkFcst.ml,v $ ******************************************************************************* ** $Id: checkFcst.ml,v 1.1 2004/02/13 04:30:27 little Exp $ ******************************************************************************) open Http_client.Convenience open OUnit (* A data structure to hold forecast information. *) type forecast = { date : string; skyCond : string; highTemp : string; lowTemp : string; pop : string; } (* A variable used to store the URL where the weather is found. * * string *) let weatherURL = "http://www.wrh.noaa.gov/Saltlake/climate/STP.shtml" (* Get the raw text from the given url. * * string -> string *) let getPage url = http_get url (* Get the test data and put it into a string. * * string *) let getTestPage _ = let fileName = "testWeather.txt" in let fin = open_in fileName in let buf = Buffer.create 4096 in let rec loop() = let line = input_line fin in Buffer.add_string buf line; Buffer.add_char buf '\n'; loop() in try loop() with End_of_file -> Buffer.contents buf (* A function that takes the forecast web page and parses out the * useful information. * * string -> string *) let getPreData data = let startPrePos = Str.search_forward (Str.regexp "
") data 0 in
  let firstPre = Str.string_after data startPrePos in
  let endPrePos = Str.search_forward (Str.regexp "
") firstPre 0 in let preTagData = Str.string_before firstPre endPrePos in let startWeather = Str.search_forward (Str.regexp "SALT LAKE CITY +: +") preTagData 0 in let dayStr = Str.string_after preTagData (Str.match_end()) in let endSLC = Str.search_forward (Str.regexp "[A-Z]") dayStr 0 in Str.string_before dayStr endSLC (* Test getPreData *) let testGetPreData _ = let p = getTestPage () in let preData = getPreData p in assert_equal preData "24 / -4 / 0.00 / 0 / 7 " (* Parse the data generated by getPreData. * * string -> forecast *) let parsePreData s = let time = Unix.localtime (Unix.time()) in let {Unix.tm_yday = yday;} = time in let weatherL = Str.split (Str.regexp " +/ +") s in { date = (string_of_int yday); skyCond = ""; highTemp = List.nth weatherL 0; lowTemp = List.nth weatherL 1; pop = "" } (* Test parsePreData *) let testParsePreData _ = let time = Unix.localtime (Unix.time()) in let {Unix.tm_yday = yday;} = time in let p = getTestPage () in let preData = getPreData p in let res = parsePreData preData in assert_equal res { date = (string_of_int yday); skyCond = ""; highTemp = "24"; lowTemp = "-4"; pop = "" } (* Output the forecast to a string. * * forecast -> string *) let printFcstOut f = Printf.sprintf "%s\t" f.date ^ Printf.sprintf "%s\t" f.skyCond ^ Printf.sprintf "%s\t" f.highTemp ^ Printf.sprintf "%s\t" f.lowTemp ^ Printf.sprintf "%s\n" f.pop (* Output the forecast to a string with numbered fields. * * forecast -> string *) let printFcstOutNum f = Printf.sprintf "1:%s\t" f.date ^ Printf.sprintf "2:%s\t" f.skyCond ^ Printf.sprintf "3:%s\t" f.highTemp ^ Printf.sprintf "4:%s\t" f.lowTemp ^ Printf.sprintf "5:%s\n" f.pop (* Output the forecastList data structure to the given channel. * * forecast list -> out_channel -> unit *) let outputFcstL l chan = let time = Unix.localtime (Unix.time ()) in let { Unix.tm_mon = mon; Unix.tm_mday = mday; Unix.tm_year = year; Unix.tm_yday = yday; Unix.tm_hour = hour; Unix.tm_min = min} = time in Printf.fprintf chan "Collected: %02d-%02d-%d @ %d:%d\n" (mon+1) mday (year+1900) hour min; Printf.fprintf chan "Date\tWeather\tHigh\tLow\tProb. of Precip.\n"; Printf.fprintf chan "%s" (String.concat "" (List.map printFcstOut l)) (* Create a fcst record from a given line of the output file. * * string -> forecast *) let fcst_of_line fcst = let fcstL = Str.split (Str.regexp "\t") fcst in { date = List.nth fcstL 0; skyCond = List.nth fcstL 1; highTemp = List.nth fcstL 2; lowTemp = List.nth fcstL 3; pop = List.nth fcstL 4 } (* Gets line # line from the provided input channel * * in_channel -> int -> string *) let rec getLine fin line = match line with 0 -> input_line fin | i -> let s = input_line fin in getLine fin (line - 1) (* Determine the goodness of a forecast. * * forecast -> int -> int -> int -> unit *) let calcGoodness res yday year pos = let fileName = (string_of_int (yday - pos)) ^ "-" ^ (string_of_int (1900 + year)) ^ ".fcst" in let fin = open_in fileName in let line = getLine fin (pos + 2) in let fcst = fcst_of_line line in match (((abs (int_of_string (fcst.highTemp) - int_of_string (res.highTemp))) > 4) && ((abs (int_of_string (fcst.lowTemp) - int_of_string (res.lowTemp))) > 4)) with true -> Printf.printf "Bad forecast %d day(s) ago.\n" (pos+1) | false -> Printf.printf "Good forecast %d day(s) ago.\n" (pos+1) (* Checks the goodness of a forecast for a given number of days. * * forecast -> int -> int -> int -> int -> unit *) let rec checkGoodness res days yday year pos = match (pos = days) with true -> () | false -> calcGoodness res yday year pos; checkGoodness res days yday year (pos + 1) (* Check the forecast for the given number of days. * * unit *) let checkFcst = let p = getPage weatherURL in let preData = getPreData p in let res = parsePreData preData in let days = int_of_string(Sys.argv.(2)) in let time = Unix.localtime (Unix.time()) in let {Unix.tm_yday = yday; Unix.tm_year = year} = time in checkGoodness res days (yday-1) year 0 let testCheckFcst _ = assert_equal "foo" "foo" let suite = "Testing checkFcst" >::: ["testParsePreData" >:: testParsePreData; "testGetPreData" >:: testGetPreData] let _ = let verbose = ref false in let set_verbose _ = verbose := true in Arg.parse [("-verbose", Arg.Unit set_verbose, "Run the test in verbose mode.");] (fun x -> ()) ("usage: " ^ Sys.argv.(0) ^ " [-verbose]"); if not (was_successful (run_test_tt ~verbose:!verbose suite)) then exit 1