Last active
April 7, 2024 10:53
-
-
Save MrModest/9bdd7651cc62d240f3f5874473bf6e98 to your computer and use it in GitHub Desktop.
The model structure for potential trip planner app like 'tripit.com' or 'travelerbuddy.com'.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
The idea behind it to make something like 'TravelerBuddy', but simplier. Easy to write and easy to read. | |
Just to have a chronological plan of points of interests and all crucial parts of journey. Also, to have all crucial info handy. | |
Backend is as simple as a CRUD (if you aren't planned to implement multi-user/authorization support). | |
The most work is expected on Frondend side. It needs to provide a convenient and very specific form for each TripItem. | |
And also draw a "graph" like below. | |
No need to support autocompletion for Address. It's enought to support "open in Map" feature. So even no needs in having a build-in map view. | |
Since there's no much commons between trip items (as well as no joins between them), | |
it could make sense to use a NoSQL rather than have a ton of nullable columns in one RDS table. | |
10:00 -|- Leave the house | |
| | |
10:30 -|- Departure from bus station | |
| | |
11:30 -|- Arrival to BER airport | |
| | |
| | |
13:30 -|- Flight departure | |
... | |
*/ | |
data class TimezonedDateTime ( // very important to store timezone since trips could be between timezones | |
val datetime: LocalDateTime, | |
val localTz: TimeZone | |
) { | |
fun toTz(val tz: TimeZone): LocalDateTime { // to show time according to the current timezone of the viewer | |
// ... | |
} | |
} | |
data class Trip( // simple container for trip items | |
val title: String, | |
val items: List<TripItem> | |
val start: TimezonedDateTime, | |
val end: TimezonedDateTime | |
) | |
data class DocumentLink( | |
val name: String, | |
val link: String // GoogleDrive Link, for example | |
) | |
data class TimelinePoint ( | |
val name: String, | |
val time: TimezonedDatetime, | |
val address: Address | |
) | |
interface TripItem { // abstract entity to represent different type of components of the trip | |
val id: UUID, | |
val note: String, // markdown - to support reference links | |
val attachments: List<DocumentLink>, | |
val timelinePoints: List<TimelinePoint> | |
} // all these fields should be represented in any inherited classes, ommited just for simplisity | |
data class Person ( // could as participant of the trip as well as any involved person. | |
val name: String, | |
val contact: String, | |
val note: String | |
) | |
data class MapPoint( | |
val longitude: String, | |
val latitude: String | |
) | |
data class Address( | |
val country: String, | |
val city: String, | |
val address: String, | |
val mapPoint: MapPoint | |
) { | |
val None: Address = Address("None", "None", "None", MapPoint("0", "0")) | |
} | |
enum class AirportCode { | |
LEJ, AYT //, etc.. | |
} | |
data class Airport( | |
val code: AirportCode, | |
val name: String, | |
val address: Address | |
) | |
data class AirportPoint( | |
val airport: Airport, | |
val terminal: String, | |
val gate: String, | |
val time: TimezonedDateTime, | |
) | |
data class Flight: TripItem ( | |
val flightNumber: String, | |
val carrier: String, | |
val bookingCode: String, | |
val seat: String, | |
val passengers: List<Person>, | |
val departure: AirportPoint, | |
val arrival: AirportPoint | |
// (?) connected (next) flight with type `Flight` | |
// (?) return flight with type `Flight` | |
// similar for LongLandTransfer | |
) { | |
override val timelinePoints: List<TimelinePoint> | |
get = listOf( | |
TimelinePoint( | |
"Flight from [${departure.airport.code}]", | |
departure.time, | |
departure.airport.address | |
), | |
TimelinePoint( | |
"Flight to [${arrival.airport.code}]", | |
arrival.time, | |
arrival.airport.address | |
) | |
) | |
} | |
data class Hotel: TripItem ( | |
val name: String, | |
val address: Address, | |
val reservationOn: Person, // the person who need to contact with hotel | |
val guests: List<Person>, | |
val numberOfRooms: Int, | |
val contacts: String // phone; email | |
val checkIn: TimezonedDateTime, // it's about planned time, not available from hotel | |
val checkOut: TimezonedDateTime // for example, hotel offers check out from 11:00, but you want to leave at 8:00. So here should be set 8:00. | |
) { | |
override val timelinePoints: List<TimelinePoint> | |
get = listOf( | |
TimelinePoint( | |
"Check-In [${name}]", | |
checkIn, | |
address | |
), | |
TimelinePoint( | |
"Check-Out [${name}]", | |
checkIn, | |
address | |
) | |
) | |
} | |
data class PublicTransportConnections( // BUS 165 -> S 45 -> RE 5(3345) | |
val time: TimezonedDateTime, // [{ time: "2024-01-10 09:00", decriptions: "BUS 165" }, { time: "2024-01-10 10:00", decriptions: "S 45" }, { time: "2024-01-10 11:30", decriptions: "RE 5(3345)" }] | |
val description: String, | |
val point: Address = Address.None // no need to bother filling it every time | |
) | |
data class PublicTransport: TripItem ( // includes all potential changes, so use only one item even if you need to change from bus to train and then to tram. | |
val startPoint: Address, | |
val endPoint: Address, | |
val departure: TimezonedDateTime, // could be approximate | |
val arrival: TimezonedDateTime, // could be approximate | |
val approximateDuration: String, // 1h 13 min | |
val connections: PublicTransportConnections[], // optional | |
) { | |
override val timelinePoints: List<TimelinePoint> | |
get = listOf( | |
TimelinePoint( | |
"Start commute to '${endPoint.address}' [${approximateDuration}]", | |
departure, | |
startPoint | |
), | |
*connections.map { | |
TimelinePoint( | |
it.description, | |
it.time, | |
it.point | |
) | |
}, | |
TimelinePoint( | |
"End commute to '${endPoint.address}' [${approximateDuration}]", | |
arrival, | |
endPoint | |
) | |
) | |
} | |
enum class TransferType { | |
Bus, Train, Ferry | |
} | |
data class LongLandTransfer: TripItem ( // it's about long transfers by land like train or intercity buses. Somethings where punctuality is crucial | |
val transferType: TransferType, | |
val transferNumber: String, | |
val carrier: String, | |
val contacts: String, | |
val passengers: List<Person> | |
val pickUpTime: TimezonedDateTime, | |
val pickUpLocation: Address, | |
val dropOffTime: TimezonedDateTime, | |
val dropOffLocation: Address | |
) { | |
override val timelinePoints: List<TimelinePoint> | |
get = listOf( | |
TimelinePoint( | |
"[${carrier}] ${transferType} from ${pickUpLocation.address}", | |
pickUpTime, | |
pickUpLocation | |
), | |
TimelinePoint( | |
"[${carrier}] ${transferType} to ${dropOffLocation.address}", | |
dropOffTime, | |
dropOffLocation | |
) | |
) | |
} | |
data class GeneralPoint: TripItem ( // any point you want to mark on your journey map. For example, the very first and very last points of your trip. | |
val name: String, | |
val address: Address, | |
val beHereAt: TimezonedDateTime | |
) { | |
override val timelinePoints: List<TimelinePoint> | |
get = listOf( | |
TimelinePoint( | |
"Be at ${address.address}", | |
beHereAt, | |
address | |
) | |
) | |
} | |
data class ObservationEvent: TripItem ( // not related to us, but important to observe, like friend's flight | |
val name: String, | |
val startTime: TimezonedDateTime, | |
val endTime: TimezonedDateTime, | |
val address: Address? | |
) { | |
override val timelinePoints: List<TimelinePoint> | |
get = listOf( | |
TimelinePoint( | |
"Start [${name}]", | |
startTime, | |
address | |
), | |
TimelinePoint( | |
"End [${name}]", | |
endTime, | |
address | |
) | |
) | |
} | |
data class Visiting: TripItem ( // visiting friends or places | |
val description: String, | |
val startTime: TimezonedDateTime, | |
val endTime: TimezonedDateTime, | |
val address: Address | |
val persons: List<Person> // relevant if visiting someone, empty if no ther people involved | |
) { | |
override val timelinePoints: List<TimelinePoint> | |
get = listOf( | |
TimelinePoint( | |
"Start [${description}]", | |
startTime, | |
address | |
), | |
TimelinePoint( | |
"End [${description}]", | |
endTime, | |
address | |
) | |
) | |
} |
Keep an individual check-list per trip item. And then provide with one accumulated check-list.
For example, under the flight you can have "take passport" and "check hand luggage restrictions compliancy". But for a commute from airport to the hotel - "take discount voucher for airport rail express that I handed over from my friend".
And all 3 stuff user can see in one combined place as requirements before the trip starts
---
title: Antalya (Fethiye)
start:
date: 2024-02-02
timezone: UTC+1
end:
date: 2024-02-13
timezone: UTC+1
items:
- id: d70e08f8-6504-4400-a0d4-15f338f8a043
type: PublicTansport
note: Commute from Home to the hotel in Leipzig
attachments: []
metadata:
startPoint:
country: Germany
city: Berlin
address: Kastanienallee 21, 12500 Berlin
mapPoint:
longitude: '72.4544810685576'
latitude: '41.51634385867111'
endPoint:
country: Germany
city: Leipzig
address: Leipzig Hbf
mapPoint:
longitude: '92.4544810685576'
latitude: '93.51634385867111'
departure:
date: 2024-02-02
time: 19:45
timezone: UTC+1
arrival:
date: 2024-02-02
time: 22:43
timezone: UTC+1
approximateDuration: 1h 13 min
commuteDescription: BUS 165 -> S 45 -> RE 5(3345)
- id: ff6476ec-da38-4c74-9bb6-df33f2e382ff
type: Hotel
note: Hotel in Leipzig
attachments:
- name: Booking_1234.pdf
link: https://drive.google.com/path/to/file.pdf
metadata:
hotelName:
address:
country: Germany
city: Leipzig
address: Kastanstraße 42, 12211 Leipzig
mapPoint:
longitude: '62.4544456785576'
latitude: '33.51634444867111'
reservationOn:
name: Katrin Mustermann
contact: +49 177 1234567
note: sample note
guests:
- name: Katrin Mustermann
contact: +49 177 1234567
note: sample note
- name: Mark Mustermann
contact: +49 177 2345678
numberOfRooms: 1
checkIn:
date: 2024-02-02
time: 22:50
timezone: UTC+1
checkOut:
date: 2024-02-03
time: 15:00
timezone: UTC+1
Timeline design guildlines: https://experience.sap.com/fiori-design-android/timeline-view/
Instead of the month in the gray line, we can show timezone change warning

P.S.: Made in Lunacy
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For sync, each item (and derivatives) should have 'syncData` property with following fields: createdDate, updatedDate, createdDeviceName, updatedDeviceName.