Python and rata.digitraffic API #2

JunaParser class

In the first post I discussed the motivation and background and also the main script. In this post I will discuss and show my implementation of the JunaParser class (Juna = train). It may not be the best but as stated earlier, I’m taking my first steps on this journey and I’m not aware of all the best practices nor the subtleties of Python.

import simplejson as json
import sys
import os
import datetime

class JunaParser():
    json_file = None
    data = {}
    def __init__(self, input_file):    
        self.json_file = input_file
        try: ## Open the file as json
            with open(self.json_file,'r') as f:
                self.data = json.load(f)
        except IOError:
            print ('This is not a real file!')
            print (self.json_file)
            sys.exit(-1)
        except Exception, e:
            print (e)

The parser is initiated with the input file and basic error handling is incorporated. I have only taken the non-file error into account. Should any other exception being raised then it’s outputted to the screen. This is sufficient for this project, so far.

Class methods

As shown in the first post, there are few methods in this class, most of them responsible for getting the train related information and printing it to the screen.

@staticmethod
    def print_time(timeToFix): 
        '''
        This method takes in the datetime (with tz UTC) from the data and returns UTC-3 time.
        Because of difficult time arithmethic a dummy date 1900-1-1 is used.
        '''
        
        timeTemp=datetime.datetime(1900,1,1,int(timeToFix[11:13]), int(timeToFix[14:16]), int(timeToFix[17:19])) + datetime.timedelta(hours=3)
        return timeTemp.strftime("%H:%M:%S")    
    
    def get_trains(self): 
        '''
        This method returns a list of trains for the defined JunaParser. 
        '''
        trainInfo = []
        j= 0
        for item in self.data:
            if self.data[j]['trainType'] not in ['MV','T','PAI','SAA','TYO','MUV','LIV']:
                trainInfo.append(self.data[j]['trainNumber'])
            j+=1 
        return trainInfo

The comments are hopefully sufficient but I will explain the reasoning behind them.

  • print_time is needed as the source data is in UTC time and I want to show the arrivals and departures in the local time. This took unreasonable time to figure out and is not perfect yet. I still need to figure out the daytime savings but I’ll tackle that when the winter time is upon us. The print_time is static because a) I wanted to test that and b) I don’t see point of having to include self to the call as it’s not called outside this class.
  • get_trains gets all the passenger trains, or more precisely excludes all non-passenger trains. Could work either way, I used the “not in” as I was interested to see which trains are NOT passenger trains and decided to code them here. The rational for the this method is that we query the arrivals/departures for the trains in scope instead if getting all the time and THEN figure out what to show.

The rest of the methods are for extracting the data from the source into the screen. Hence the tabulators are return in the case of no data.

def get_start_station(self, trainNumber): 
        '''
        This method returns the starting station for a specific train.
        Argument : TrainNumber
        '''
        i=0
        for item in self.data:
            if (str(self.data[i]['trainNumber']) == str(trainNumber)):
                return self.data[i]['timeTableRows'][0]['stationShortCode']
            else:
                pass
            i +=1
        return "Not Found" 
        
    def get_end_station(self, trainNumber):  # returns the last station for specific train
        '''
        This method returns the end station for a specific train.
        Argument : TrainNumber
        '''
        i=0
        for item in self.data:
            if (str(self.data[i]['trainNumber']) == str(trainNumber)):
                return self.data[i]['timeTableRows'][-1]['stationShortCode']
            else:
                pass
            i +=1
        return "Not Found" 
        
    def get_arrival_time(self, trainNumber, station): # returns the arrival time of a train to a specific station
        '''
        This method returns the arrival time to a specific station for a specific train.
        Argument [1] : trainNumber
        Argument [2] : station        
        '''        
        i=0
        for item in self.data:
            if (str(self.data[i]['trainNumber']) == str(trainNumber)):
                j=0
                for item in self.data[i]['timeTableRows']:
                    if (self.data[i]['timeTableRows'][j]['type'] == 'ARRIVAL'):
                        if (self.data[i]['timeTableRows'][j]['stationShortCode'] == station):
                            arrivalTime = self.print_time(self.data[i]['timeTableRows'][j]['scheduledTime'])
                            return arrivalTime
                    else:
                        pass
                    j+=1
            else:
                pass
            i +=1
        return " \t "
    
    def get_departure_time(self, trainNumber, station): # returns the departure time of a train from a specific station
        '''
        This method returns the departure time from a specific station for a specific train.
        Argument [1] : trainNumber
        Argument [2] : station
        '''        
        i=0
        for item in self.data:
            if (str(self.data[i]['trainNumber']) == str(trainNumber)):
                j=0
                for item in self.data[i]['timeTableRows']:
                    if (self.data[i]['timeTableRows'][j]['type'] == 'DEPARTURE'):
                        if (self.data[i]['timeTableRows'][j]['stationShortCode'] == station):
                            arrivalTime = self.print_time(self.data[i]['timeTableRows'][j]['scheduledTime'])
                            return arrivalTime
                    else:
                        pass
                    j+=1
            else:
                pass
            i +=1
        return " \t "
        
    def get_exp_departure_time(self, trainNumber, station): # returns the estimated departure time of a train from a specific station. If that has not been given returns empty string
        '''
        This method returns the expected departure time from a specific station for a specific train.
        If there is no 'liveEstimateTime' in the data it returns blank with tab
        Argument [1] : trainNumber
        Argument [2] : station
        '''                
        i=0
        for item in self.data:
            if (str(self.data[i]['trainNumber']) == str(trainNumber)):
                j=0
                for item in self.data[i]['timeTableRows']:
                    if (self.data[i]['timeTableRows'][j]['type'] == 'DEPARTURE'):
                        if (self.data[i]['timeTableRows'][j]['stationShortCode'] == station):
                            if('differenceInMinutes' in self.data[i]['timeTableRows'][j]) & ('liveEstimateTime'in self.data[i]['timeTableRows'][j]):
                                expDepartureTime = self.print_time(self.data[i]['timeTableRows'][j]['liveEstimateTime'])
                                return expDepartureTime
                    else:
                        pass
                    j+=1
            else:
                pass
            i +=1
        return " \t "
        
    def get_exp_arrival_time(self, trainNumber, station):  # returns the estimated arrival time of a train to a specific station. If that has not been given returns empty string
        '''
        This method returns the expected arrival time to a specific station for a specific train.
        If there is no 'liveEstimateTime' in the data it returns blank with tab
        Argument [1] : trainNumber
        Argument [2] : station
        '''        
        i=0
        for item in self.data:
            if (str(self.data[i]['trainNumber']) == str(trainNumber)):
                j=0
                for item in self.data[i]['timeTableRows']:
                    if (self.data[i]['timeTableRows'][j]['type'] == 'ARRIVAL'):
                        if (self.data[i]['timeTableRows'][j]['stationShortCode'] == station):
                            if('differenceInMinutes' in self.data[i]['timeTableRows'][j]) & ('liveEstimateTime'in self.data[i]['timeTableRows'][j]):
                                expArrivalTime = self.print_time(self.data[i]['timeTableRows'][j]['liveEstimateTime'])
                                return expArrivalTime
                    else:
                        pass
                    j+=1
            else:
                pass
            i +=1
        return " \t "
  • get_start_station does what the name suggests. As the data is sorted chronologically it means that the first item of the dict is the starting station.
  • get_end_station is the opposite and it gets the last item of the dict.
  • get_arrival_time and get_departure_time are straight-forwards as well.
  • get_exp_arrival_time and get_exp_departure_time are queried as (unfortunately) some trains are running late. It is appended into the source data automatically and is part of the dict ONLY if there is a value. Otherwise it’s missing.

Conclusion

It took some time to get going but in the end this was a nice and useful mini project which thought me quite a bit of working with APIs and Python. Nothing fancy was done but I’m planning to add GUI and porting this to my Raspberry Pi with 3.5″ screen. The idea is to have this running at home so I know when to not to dash to the station when the train is clearly late.

Should this idea ever materialise, I will share my experience 🙂

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s