2425-2ihif-pose-classroom-ex-inh-05-transport-inh-05-transport-template created by GitHub Classroom
| Data | ||
| pics | ||
| Transport | ||
| Transport.Test | ||
| .editorconfig | ||
| .gitignore | ||
| readme.adoc | ||
| Transport.sln | ||
[](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]