{"id":16644137,"url":"https://github.com/shellophobia/singaporemrt","last_synced_at":"2026-04-26T14:31:08.465Z","repository":{"id":93740302,"uuid":"331527536","full_name":"shellophobia/singaporemrt","owner":"shellophobia","description":null,"archived":false,"fork":false,"pushed_at":"2021-01-21T05:58:01.000Z","size":4482,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2023-03-01T07:25:34.672Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shellophobia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-21T05:45:33.000Z","updated_at":"2024-06-19T12:36:52.317Z","dependencies_parsed_at":"2023-04-03T12:32:43.179Z","dependency_job_id":null,"html_url":"https://github.com/shellophobia/singaporemrt","commit_stats":null,"previous_names":[],"tags_count":0,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellophobia%2Fsingaporemrt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellophobia%2Fsingaporemrt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellophobia%2Fsingaporemrt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellophobia%2Fsingaporemrt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shellophobia","download_url":"https://codeload.github.com/shellophobia/singaporemrt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243172100,"owners_count":20247887,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-12T08:10:32.776Z","updated_at":"2025-12-24T15:07:21.812Z","avatar_url":"https://github.com/shellophobia.png","language":"Go","readme":"# Overview\nThis assignment is built in Golang language and the http server is a binary file\nthat can be generated based on the platform it has to be run on.\n\u003cbr /\u003e\n\nThe HTTP server will run on http://localhost:8080 which has 1 API GET /trainRoutes which will return a list of routes.\n\u003cbr /\u003e\nThe documentation is available below for the API usage and contract\n\n# Steps to run the server\n* Set the ENV variable \"STATION_MAP_PATH\" in the system\n    ```shell script\n      export STATION_MAP_PATH=\u003cthe path to StationMap.csv file\u003e\n    ```\n* Execute the file \"server\"\n    ```shell script\n      ./server\n      This will start the http server on port 8080\n    ```\n  \n# API\n### GET /trainRoutes\n\n#### Curl\n```shell script\ncurl --location --request GET 'http://localhost:8080/trainRoutes?source=Boon%20Lay\u0026destination=Little%20India\u0026startTime=2019-01-31T08:00'\n```\n\n#### Request params\n```json\n{\n    \"source\": \"Boon Lay\",\n    \"destination\": \"Little India\",\n    \"startTime\": \"2019-01-31T08:00\" # Optional. If not provided the routes returned won't have estimated time. The time format has to be YYYY-MM-DDTHH:mm \n}\n```\n\n#### Response\n```json\n{\n    \"source\": \"Boon Lay\",\n    \"destination\": \"Little India\",\n    \"suggestedRoutes\": [\n        {\n            \"stationsTravelled\": 13,\n            \"route\": [\"EW27\",\"EW26\",\"EW25\",\"EW24\", \"EW23\",\"EW22\",\"EW21\",\"CC22\",\"CC21\",\"CC20\",\"CC19\",\"DT9\",\"DT10\",\"DT11\",\"DT12\"],\n            \"verboseRoute\": [\n                \"Take EW line from Boon Lay to Lakeside\",\n                \"Take EW line from Lakeside to Chinese Garden\",\n                \"Take EW line from Chinese Garden to Jurong East\",\n                \"Take EW line from Jurong East to Clementi\",\n                \"Take EW line from Clementi to Dover\",\n                \"Take EW line from Dover to Buona Vista\",\n                \"Change from EW line to CC line\",\n                \"Take CC line from Buona Vista to Holland Village\",\n                \"Take CC line from Holland Village to Farrer Road\",\n                \"Take CC line from Farrer Road to Botanic Gardens\",\n                \"Change from CC line to DT line\",\n                \"Take DT line from Botanic Gardens to Stevens\",\n                \"Take DT line from Stevens to Newton\",\n                \"Take DT line from Newton to Little India\"\n             ],\n            \"estimatedTimeInMinutes\": 150,\n            \"shortestRoute\": true // This is determine based on estimated time if startTime param is provided in api request else it will be based on number of stations\n        },\n        // .... other routes\n    ]\n}\n```\n\u003cbr /\u003e\n\n### Code structure\n#### Handlers\nThis package serves as a controller layer which can have validations on the API request. The logic if reusable by multiple handlers can be added into \"logic\" package\n\n#### Utils\nThis package consists of the common utility helper functions\n\n#### Common\nThis package has the common types shared across the project\n\n### Overview of trainRoutes logic\nOn package initialisation the train line graph with the stations is generated using the linked list data structure\nWhere a station/node has following attributes\n```text\n{\n  Code // this param holds the station code\n  Name // this param holds the station name\n  OpeningDate // The opening date that is present in csv file. Although this isn't used anywhere in the logic\n  PrevStation // This is a pointer to the previous station in the same train line\n  NextStation // This is a pointer to next station in the same train line\n  LinkedStations // This is a pointer list of station nodes that are linked to the station to change to a different line\n}\n```\nThe traversal to find the routes happens using Breadth first search.\n\u003cbr /\u003e To ensure that the discovery of path is faster, the traversal is done in both directions.\n\u003cbr /\u003e When a visited node is found in any of the traversal from the other traversal. e.g. In a backward traversal a node is found which was already traversed in forward traversal then if the route is operational it is considered eligible for a potential route\n\n\u003cbr /\u003e There are helper functions in the file that are used to find the estimated travel time based on the rules defined for different operating hours for the week.\n\u003cbr /\u003e The structure is defined with train line as the key and if there's a new override to be made in future for a time period that affects multiple train stations, then the train lines would have to be updated\n\n#### Structure\n\n```text\n{\n\t\u003cTrainLineCode\u003e: {\n\t\t\u003cTimeRange\u003e: {\n\t\t\tNextStationTimeInMinutes: // This will give the estimated time to get to the next station\n\t\t\tLineChangeTimeInMinutes: // This will give the estimated time to change the line on the same station\n\t\t\tDaysOfWeek: // This is an array of the days of the week to which the time range config applies\n\t\t\tIsNotOperational: // Boolean to denote whether the line is operational in the time range. Since golang's default value for boolean is false\n\t\t\t\t\t\t\t  // The value is only specified if line is not operational\n\t\t}\n\t}\n}\n```\n\n#### Considerations for train operating hours rules\n* There is a default key at trainline level which means that all the train lines that don't have a specific rule would fall under the default rules\n* There is also a default key under the trainline object which is a default timerange, meaning for that trainline what is the default behaviour if it doesn't fall any under time range check\n* To denote which days is the time range applicable for, the weekdays have to be listed out in the DaysOfWeek attribute as an array e.g. [\"Sunday\", \"Monday\", ...]\n* To mark if the train line is not operational in the time duration, a boolean flag IsNotOperational has been kept\n\n#### Potential Improvement\nThe logic can further be optimised by caching the paths between 2 stations and using them to reduce computation time.\n\n### Running the server without binary\nTo build the go server code locally and run\n* https://golang.org/doc/install follow this\n* For the ease of use, preferred editor is Goland IDE https://www.jetbrains.com/go/download/#section=mac\n* If you're using the Goland IDE you would just need to go the main.go file and run it by using the play button next to `func main` which will start the server.\n* Otherwise you can execute\n    ```shell script\n      go run main.go\n    ```\n  from terminal\n* For running test cases, if you're using Goland IDE, you would be able to execute the tests in get_routes_test.go by using the play button next to test case name.\n* Else you can follow https://golangcode.com/run-one-test/ to execute the test file\n\u003cbr /\u003e\n\n#### Command used to generate binary file\n```shell script\n   env GOOS=linux GOARCH=amd64 go build -i -o server .\n```\nThis needs to be run from the parent package that has main.go file\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshellophobia%2Fsingaporemrt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshellophobia%2Fsingaporemrt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshellophobia%2Fsingaporemrt/lists"}