Logic updates
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
module Controller
|
||||
open System
|
||||
open System.Net.Http
|
||||
open System.Text.RegularExpressions
|
||||
open NextFlight.Types
|
||||
open FSharp.Collections
|
||||
open HtmlAgilityPack
|
||||
@@ -36,7 +35,7 @@ let private formatDateReadable (date: DateTime) (timezone: string) =
|
||||
// Helper to extract city from "City / Airport" format
|
||||
let private extractCity (location: string) =
|
||||
match location.Split('/') with
|
||||
| parts when parts.Length > 0 -> parts.[0].Trim()
|
||||
| parts when parts.Length > 0 -> parts[0].Trim()
|
||||
| _ -> location
|
||||
|
||||
let private calculateArrivalDate (departureDate: DateTime) (departureTime: string) (arrivalTime: string) =
|
||||
@@ -46,10 +45,10 @@ let private calculateArrivalDate (departureDate: DateTime) (departureTime: strin
|
||||
let arrParts = arrivalTime.Split(':')
|
||||
|
||||
if depParts.Length = 2 && arrParts.Length = 2 then
|
||||
let depHour = Int32.Parse(depParts.[0])
|
||||
let depMinute = Int32.Parse(depParts.[1])
|
||||
let arrHour = Int32.Parse(arrParts.[0])
|
||||
let arrMinute = Int32.Parse(arrParts.[1])
|
||||
let depHour = Int32.Parse(depParts[0])
|
||||
let depMinute = Int32.Parse(depParts[1])
|
||||
let arrHour = Int32.Parse(arrParts[0])
|
||||
let arrMinute = Int32.Parse(arrParts[1])
|
||||
|
||||
let depTimeOfDay = TimeSpan(depHour, depMinute, 0)
|
||||
let arrTimeOfDay = TimeSpan(arrHour, arrMinute, 0)
|
||||
@@ -67,8 +66,23 @@ let private calculateArrivalDate (departureDate: DateTime) (departureTime: strin
|
||||
// If any error in parsing, assume same day
|
||||
departureDate
|
||||
|
||||
let getLogoUrl rawFlight = $"https://content.airhex.com/content/logos/airlines_{rawFlight.AirlineCode}_60_60_t.png"
|
||||
|
||||
let removeDuplicateWords (string: string) =
|
||||
string.Split(' ')
|
||||
|> Array.fold (fun acc word ->
|
||||
match acc with
|
||||
| [] -> [word]
|
||||
| head :: _ when head = word -> acc
|
||||
| _ -> word :: acc
|
||||
) []
|
||||
|> List.rev
|
||||
|> String.concat " "
|
||||
|
||||
// Map RawFlight to Flight
|
||||
let private mapToFlight (raw: RawFlight) (timezone: string) : Flight =
|
||||
|
||||
|
||||
let arrivalDate = calculateArrivalDate raw.Date raw.DepartureTime raw.ArrivalTime
|
||||
{
|
||||
departureAirportCode = raw.FromCode
|
||||
@@ -80,8 +94,10 @@ let private mapToFlight (raw: RawFlight) (timezone: string) : Flight =
|
||||
arrivalDateReadable = formatDateReadable arrivalDate timezone
|
||||
arrivalTime = raw.ArrivalTime
|
||||
flightNumber = raw.FlightNumber
|
||||
airlineCode = raw.AirlineCode
|
||||
airlineName = raw.Airline
|
||||
aircraftType = raw.Aircraft
|
||||
aircraftType = removeDuplicateWords raw.Aircraft
|
||||
logoUrl = getLogoUrl raw
|
||||
}
|
||||
|
||||
let private parseTableRow (row: HtmlNode) : RawFlight option =
|
||||
@@ -91,53 +107,54 @@ let private parseTableRow (row: HtmlNode) : RawFlight option =
|
||||
if cells = null || cells.Count < 13 then None
|
||||
else
|
||||
// Extract date
|
||||
let dateNode = cells.[0].SelectSingleNode(".//span[@class='inner-date']")
|
||||
let dateNode = cells[0].SelectSingleNode(".//span[@class='inner-date']")
|
||||
if dateNode = null then None
|
||||
else
|
||||
let dateStr = dateNode.InnerText.Trim()
|
||||
let date = DateTime.Parse(dateStr)
|
||||
|
||||
// Flight number
|
||||
let flightNumber = getInnerText (Some cells.[1])
|
||||
let flightNumber = getInnerText (Some cells[1])
|
||||
|
||||
// Registration
|
||||
let registration = getInnerText (Some cells.[2])
|
||||
let registration = getInnerText (Some cells[2])
|
||||
|
||||
// From airport
|
||||
let fromNode = cells.[3].SelectSingleNode(".//span[@class='tooltip']")
|
||||
let fromNode = cells[3].SelectSingleNode(".//span[@class='tooltip']")
|
||||
let fromCode = getInnerText (Some fromNode)
|
||||
let fromFull = getAttributeValue (Some fromNode) "data-tooltip-value"
|
||||
|
||||
// To airport
|
||||
let toNode = cells.[4].SelectSingleNode(".//span[@class='tooltip']")
|
||||
let toNode = cells[4].SelectSingleNode(".//span[@class='tooltip']")
|
||||
let toCode = getInnerText (Some toNode)
|
||||
let toFull = getAttributeValue (Some toNode) "data-tooltip-value"
|
||||
|
||||
// Distance (remove commas)
|
||||
let distanceStr = getInnerText (Some cells.[5])
|
||||
let distanceStr = getInnerText (Some cells[5])
|
||||
let distance =
|
||||
if String.IsNullOrWhiteSpace(distanceStr) then 0
|
||||
else Int32.Parse(distanceStr.Replace(",", ""))
|
||||
|
||||
// Times
|
||||
let depTime = getInnerText (Some cells.[6])
|
||||
let arrTime = getInnerText (Some cells.[7])
|
||||
let depTime = getInnerText (Some cells[6])
|
||||
let arrTime = getInnerText (Some cells[7])
|
||||
|
||||
// Airline
|
||||
let airlineNode = cells.[8].SelectSingleNode(".//span[@class='tooltip']")
|
||||
let airlineNode = cells[8].SelectSingleNode(".//span[@class='tooltip']")
|
||||
let airline = getAttributeValue (Some airlineNode) "data-tooltip-value"
|
||||
let airlineCode = getInnerText (Some cells[8])
|
||||
|
||||
// Aircraft
|
||||
let aircraftNode = cells.[9].SelectSingleNode(".//span[@class='tooltip']")
|
||||
let aircraftNode = cells[9].SelectSingleNode(".//span[@class='tooltip']")
|
||||
let aircraft = getAttributeValue (Some aircraftNode) "data-tooltip-value"
|
||||
|
||||
// Seat - look for pattern like "19A" in the seat cell
|
||||
let seatText = cells.[10].InnerText
|
||||
let seatText = cells[10].InnerText
|
||||
let seatMatch = System.Text.RegularExpressions.Regex.Match(seatText, @"\d+[A-Z]")
|
||||
let seat = if seatMatch.Success then seatMatch.Value else ""
|
||||
|
||||
// Class and Reason from icons column
|
||||
let iconsCell = cells.[12]
|
||||
let iconsCell = cells[12]
|
||||
|
||||
// Class
|
||||
let classNode = iconsCell.SelectSingleNode(".//span[contains(@class, 'class-')]")
|
||||
@@ -174,6 +191,7 @@ let private parseTableRow (row: HtmlNode) : RawFlight option =
|
||||
DepartureTime = depTime
|
||||
ArrivalTime = arrTime
|
||||
Airline = airline
|
||||
AirlineCode = airlineCode
|
||||
Aircraft = aircraft
|
||||
Seat = seat
|
||||
Class = flightClass
|
||||
@@ -225,8 +243,8 @@ let GetNextFlights (username: string) (timezoneCountry: string) (timezoneCity: s
|
||||
try
|
||||
let timeParts = flight.DepartureTime.Split(':')
|
||||
if timeParts.Length = 2 then
|
||||
let hour = Int32.Parse(timeParts.[0])
|
||||
let minute = Int32.Parse(timeParts.[1])
|
||||
let hour = Int32.Parse(timeParts[0])
|
||||
let minute = Int32.Parse(timeParts[1])
|
||||
// Create departure DateTime in YOUR timezone
|
||||
let departureDateTime = DateTime(flight.Date.Year, flight.Date.Month, flight.Date.Day, hour, minute, 0)
|
||||
departureDateTime > now
|
||||
@@ -240,8 +258,8 @@ let GetNextFlights (username: string) (timezoneCountry: string) (timezoneCity: s
|
||||
try
|
||||
let timeParts = flight.DepartureTime.Split(':')
|
||||
if timeParts.Length = 2 then
|
||||
let hour = Int32.Parse(timeParts.[0])
|
||||
let minute = Int32.Parse(timeParts.[1])
|
||||
let hour = Int32.Parse(timeParts[0])
|
||||
let minute = Int32.Parse(timeParts[1])
|
||||
DateTime(flight.Date.Year, flight.Date.Month, flight.Date.Day, hour, minute, 0)
|
||||
else
|
||||
flight.Date
|
||||
|
||||
@@ -40,7 +40,7 @@ module Program =
|
||||
use_static (Path.Combine(AppContext.BaseDirectory, "wwwroot"))
|
||||
use_developer_exceptions
|
||||
service_config ServiceConfig
|
||||
url "http://0.0.0.0:5001"
|
||||
url "http://0.0.0.0:5000"
|
||||
}
|
||||
|
||||
run app
|
||||
@@ -13,6 +13,7 @@ type RawFlight = {
|
||||
DepartureTime: string
|
||||
ArrivalTime: string
|
||||
Airline: string
|
||||
AirlineCode: string
|
||||
Aircraft: string
|
||||
Seat: string
|
||||
Class: string
|
||||
@@ -29,6 +30,8 @@ type Flight = {
|
||||
arrivalDateReadable: string
|
||||
arrivalTime: string
|
||||
flightNumber: string
|
||||
airlineCode: string
|
||||
airlineName: string
|
||||
aircraftType: string
|
||||
logoUrl: string
|
||||
}
|
||||
Reference in New Issue
Block a user