2425-2ihif-pose-classroom-ex-inh-05-transport-inh-05-transport-template created by GitHub Classroom
Find a file
github-classroom[bot] f9216e116a
add deadline
2025-04-03 14:57:36 +00:00
Data Initial commit 2025-02-25 17:10:54 +00:00
pics Initial commit 2025-02-25 17:10:54 +00:00
Transport Initial commit 2025-02-25 17:10:54 +00:00
Transport.Test Initial commit 2025-02-25 17:10:54 +00:00
.editorconfig Initial commit 2025-02-25 17:10:54 +00:00
.gitignore Initial commit 2025-02-25 17:10:54 +00:00
readme.adoc add deadline 2025-04-03 14:57:36 +00:00
Transport.sln Initial commit 2025-02-25 17:10:54 +00:00

[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/D1c8q0tI)
:sectnums:
:nofooter:
:toc: left
:icons: font
:data-uri:
:source-highlighter: highlightjs
:stem: latexmath

= Inh.05 -- Transport

You need to implement a management system for a vehicle rental business.
Customers can rent a vehicle for a trip.
For that they have to pay a fee which is dependent on various factors.
The vehicles incur costs for the company.
The goal is to make a profit!

The _special_ feature of this exercise is that you will read & create the most important data format for every company all over the world: **Excel documents** 😮

WARNING: The specifics of this assignment did not allow to provide extensive unit tests -- otherwise big parts of the implementation would have to be duplicated/visible in the tests. So you can't rely on tests, but have to check the output files to see if your implementation is looking good.

== Data Model

[plantuml]
----
@startuml
hide empty methods

abstract class Vehicle {
    -List<Ride> _rides [readonly]
    -RentalInfo? _rentalInfo
    {abstract} +decimal BasePrice [readonly]
    {abstract} +decimal PricePerKM [readonly]
    {abstract} +decimal CostPerKM [readonly]
    +Color Color [readonly]
    +int Weight [readonly]
    +int Id [readonly]
    +double Mileage [private set]
    +bool IsFree [readonly]

    #Vehicle(int, Color, int, double)
    +bool Rent(string, double)
    +(bool, decimal?) EndRide()
    +void AddTravelLogSheet(IXLWorkbook)
    {static} #ExcelData? ProcessExcelRow(IXLRow, int)
    {static} #List<IXLRow> GetRowsFromWorksheet(IXLWorksheet)
    {static} #bool TryParseDoubleInvariant(string?, out double)
    {static} #int GetColumnsOfWorksheet(IXLWorksheet)
}
class Car <<sealed>> {
    +string WorksheetName [const]
    -decimal BaseCostPerKM
    {static} -Dictionary<CarBrand, double> brandCoolness [readonly]
    +CarBrand Brand [readonly]
    +double Power [readonly]

    {static} +List<Car> CreateFromWorksheet(IXLWorksheet)
}
class Rickshaw <<sealed>> {
    +string WorksheetName [const]

    {static} +List<Rickshaw> CreateFromWorksheet(IXLWorksheet)
}
class Ride <<record,sealed>> {
    +string CustomerName [readonly]
    +Vehicle Vehicle [readonly]
    +double StartMileage [readonly]
    +double EndMileage [readonly]
    +double Distance [readonly]
    +decimal TotalCost [readonly]
    +decimal TotalPrice [readonly]

    -decimal TotalForDistance(decimal)
}
class TravelManagement <<sealed>> {
    +double MaxRickshawDistance [const]
    -List<Vehicle> _vehicles [readonly]

    +TravelManagement()
    +bool InitializeFromFile(string)
    +Vehicle? RentVehicle<TPreferredVehicle>(string, double)
    +IXLWorkbook GetTravelLog()
}
enum Color {
    Red
    Blue
    White
    Grey
    Black
}
enum CarBrand
{
    AlfaRomeo
    BMW
    Dacia
    VW
}

Vehicle <|-- Car
Vehicle <|-- Rickshaw
Vehicle o-- Ride
Ride *-- Vehicle
Vehicle *-- Color
Car *-u- CarBrand
TravelManagement o-r- Vehicle
note top of Vehicle : + Equality members

@enduml
----

== Vehicles
Currently, two types of vehicles are available:

* Rickshaws
* Cars

[cols="a,a",frame=none, grid=none]
|===
| image::pics/rickshaw.png[Rickshaw]
| image::pics/car.jpg[Car]
|===

Each vehicle has the following properties:

* `Id`
** Unique
** Cannot be stem:[<0]
* `Color`
* `Weight`
** Cannot be stem:[<0]
* `Mileage`
** Initial mileage before rides are performed
*** => will change after each ride
** Cannot be stem:[<0]

=== Rickshaw
* Has no additional properties
* Has the following, _fixed_ values:
** `BasePrice`: 2
** `PricePerKM`: 2.5
** `CostPerKM`: 2.2

=== Car
* Has the following, additional properties:
** `Brand`
** `Power` (engine power in kw)
*** Cannot be stem:[<=0]
* Has the following, _dynamic_ values:
** `BasePrice`: stem:[3 * CoolnessOfBrand]
** `PricePerKM`: stem:[CostPerKM * 1.05]
*** We want to make a profit after all
** `CostPerKM`: stem:[BaseCostPerKM * (\frac{engineFactor + weightFactor}{100} + 1)]

.CoolnessOfBrand
|===
|Brand |Coolness

|AlfaRomeo
|stem:[1.6]

|BMW
|stem:[1.1]

|Dacia
|stem:[1]

|VW
|stem:[0.6]
|===

.engineFactor
|===
|Power |Factor

|stem:[<= 66]
|stem:[0]

|stem:[<= 88]
|stem:[5]

|stem:[<= 110]
|stem:[10]

|stem:[> 110]
|stem:[20]
|===

.weightFactor
|===
|Weight |Factor

|stem:[< 1000]
|stem:[0]

|stem:[< 1500]
|stem:[10]

|stem:[< 2000]
|stem:[15]

|stem:[< 2400]
|stem:[20]

|stem:[>= 2400]
|stem:[25]
|===

== Excel
As stated before you'll have to read & write Excel files.
That is important, because almost every clerk knows Excel, so the most request import & export format in any application is always 'Excel!' -- no matter if there would be technically better suited formats.
There is also an old joke 'no matter how much code we write, the world continues to run on Excel'.

TIP: Try to open any `*.xlsx` File in a text editor: you'll realize that will be harder to process than CSV.

Contrary to simple text (or binary) files like CSV, Excel files allow for advanced formatting.
Each cell can for example have a type (e.g. format as number, as text, as date,...), a style (e.g. bold, italics, font, font size,...) or even a formula (e.g. SUM,...).
Columns, rows and cells can also be addressed by 'name', e.g. `A4`.
And finally, one file (= workbook) can contain multiple worksheets.
For this assignment we'll stick to basic features, but knowing how to read & create such complex files will be a valuable skill for you to have.

To process Excel file we are going to use a free & open source library called https://github.com/ClosedXML/ClosedXML[ClosedXML].
The appropriate https://www.nuget.org/packages/ClosedXML/[NuGet] package has already been added to your starter project.
However, learning how to use the API is mostly up to you -- _it might be a good idea to read the documentation_ 😉.
Several methods already accept or return types you'll need (e.g. `IXLWorksheet`), these are a good starting point for your research.

== Program Description

The program runs through the following steps:

. Vehicle data is read from the file `vehicles.xlsx`
** Each worksheet contains data for one type of vehicle
** Invalid rows are skipped
. Several rental operations are performed
** The ride information is stored by each vehicle
. The travel log is written to a `travel-log-{date&time}.xlsx` file
** The workbook contains multiple sheets -- one for each vehicle
*** Only vehicles with at least one ride create a sheet
** Each sheet contains the rentals/rides of one vehicle

=== Implementation Hints & Requirements

A couple of additional explanations for several, more complex methods:

* `Vehicle.EndRide`
** Ending an active rental (ride) adds this ride to the list of rides and increases the mileage accordingly
** The vehicle is then free to be rented again
* `Vehicle.GetColumnsOfWorksheet`
** Returns the number of _used_ columns in the given sheet
** A column is used if any of its cells has a non-empty value
* `TravelManagement.InitializeFromFile`
** Populates the internal list of vehicles
** Make use of the `CreateFromWorksheet` methods
** You might want to use https://learn.microsoft.com/en-us/dotnet/api/system.io.file.exists[`File.Exists`] to check if a file exists
* `TravelManagement.RentVehicle`
** Selects a _free_ vehicle of the specified type with the _lowest_ mileage of all vehicles of this type => the least worn vehicles are preferred
*** If the required distance exceeds the limit for a `Rickshaw` a `Car` is selected instead
** You can use `typeof(TPreferredVehicle)` to get the requested type
*** If you are unsure what that operator does https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast#typeof-operator[read up on it]
** To find out the _runtime type_ of an _instance_ https://learn.microsoft.com/en-us/dotnet/api/system.object.gettype?view=net-8.0[use the `GetType()`] method

TIP: Also carefully read the provided XMLDoc, it contains valueable information

== Tasks

. Learn how to use the ClosedXML library
. Complete the application
. Ensure that the sample run in `Program` leads to a result as defined below
** This includes the _formatting_ in the Excel file!

=== Sample Run

Executing `Program` should result in this console output:

[source]
----
*** Transport ***
Total cost of all rides: € 1.114,15
Travel log saved to: <path-to-file>\travel-log-<date&time>.xlsx
----

The created travel log file should contain the following worksheets with these values and the proper formatting:

image::pics/travel_log_01.png[TravelLog]
image::pics/travel_log_02.png[TravelLog]
image::pics/travel_log_03.png[TravelLog]
image::pics/travel_log_04.png[TravelLog]
image::pics/travel_log_05.png[TravelLog]
image::pics/travel_log_06.png[TravelLog]