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