Projektübersicht Hackathon

Allgemeine Regeln und Bewertungsvorgänge

  1. Jede Aufgabe wird dynamisch bepunktet.

    1. Die Abgaben zu einer Aufgabe werden zuerst gefiltert. Abgaben, die nicht eine Mindestgüte haben, werden ausgelassen.

    2. Die übrigen guten Abgaben erhalten ein Rating zwischen 0.25 und 1.

    3. Die Gesamtpunktzahl für eine Aufgabe hängt von der Anzahl der guten Abgaben ab.

    4. Details finden sich auf Discord https://discord.com/channels/806789200455794688/817334896007053322/818551537026072626

  2. Teamabgaben sind für einige Aufgaben erlaubt. Die Maximalgröße eines Teams steht hinter jeder Aufgabe. Abgaben, die von größeren Teams erstellt werden, werden nicht bewertet.

  3. Commits, die nach 14:30 Uhr am 14.03.2021 (UTC+1) in einem Abgabeprojekt gepushed werden, werden nicht mitgezählt.

    1. Falls bei der Bewertung ein Punktegleichstand unter den 3 Teilnehmern mit der größten Punktzahl entsteht, wird eine Bonusaufgabe im Ankündigungschannel angekündigt.

    2. Die drei schnellsten korrekten Abgaben dieser Bonusaufgabe entscheiden dann die Platzierung.

  4. Wenn ihr Hilfe bei der Lösung einer Aufgabe braucht, fragt bitte im #fachfragen Channel im Discord nach.

    1. Gute Antworten auf gute Fragen werden mit Punkten belohnt. Insgesamt können für Antworten auf Fragen 50% der Punkte gesammelt werden, die es für die Aufgabe mit der größten Gesamtpunktzahl gibt.

    2. Falls die Frage nach 30 Minuten nicht beantwortet und nicht selbst gelöst wurde, eskaliert sie bitte in den #fragen-mentor Channel.

    3. Fragen bezüglich der Formulierung einer Aufgabe kommen in den #fragen-aufgabenstellung Channel. Änderungen/ Erläuterungen der Aufgaben werden auch auf dieser Seite durchgeführt. Dazu wird es im #errata Channel Hinweise geben.

  5. Eine Aufgabenabgabe besteht mindestens aus einem github.com oder gitlab.com Projekt (Weitere Anforderungen finden sich in den Aufgabenkategorien)

    1. Auf höchster Ebene enthält das Projekt mindestens die Dateien Readme.md, LICENSE und Dockerfile

    2. Eine Abgabe erfolgt über das Formular https://docs.google.com/forms/d/e/1FAIpQLSfVQ6UmGHB-Y1IZIri0cfQA7ZHJNp5xzqTma0-hgNOHPS53_w/viewform

    3. Wir empfehlen euch, die Repositories als privat anzulegen, damit niemand bei euch abgucken kann. Kurz vor der Abgabe könnt ihr sie auf public stellen, damit wir sie pullen können.

    4. Siehe auch https://github.com/LukasKaufmannRelaxdays/hackathon-example-submission

  6. Eine Software lässt sich mit docker build und docker run ausführen. In der Readme.md wird dies noch etwas detailierter dokumentiert.

    1. Falls Software Filenamen per Argument übergeben bekommt, wird ein Ordner an euren Container gemounted, in dem die Testfiles liegen und die absoluten Pfade innerhalb des Containers werden per Argument übergeben.

  7. Im LICENSE File gebt ihr an, unter welchen Bedingungen eure Software zu nutzen ist. Zum Beispiel könnt ihr hier eine MIT-Lizenz verwenden.

  8. In der Readme.md ist folgender Inhalt in den ersten Zeilen zu finden und enthält eure Teilnehmer ID(s) statt "CC-VOL1-42, CC-VOL1-21".

This project was created in the Relaxdays Code Challenge Vol. 1. See https://sites.google.com/relaxdays.de/hackathon-relaxdays/startseite for more information. My (Our) participant ID(s) in the challenge was(were): CC-VOL1-42, CC-VOL1-21

Aufgaben mit viel Freiraum

Diese Aufgaben sind nicht so konkret gestellt. Hier ist prinzipiell alles erlaubt! Zu diesen Aufgaben ist ein kurzes Demonstrationsvideo zu produzieren, in welchem ihr zeigt, was eure Software alles kann. Diese Videos sollten die Länge von 5 Minuten nicht überschreiten. Verlinkt die Videos in eurer Readme.md

Die Bewertung erfolgt über ein kleines Gremium von Relaxdays-Mitarbeitern. Wir achten hier besonders auf Erweiterbarkeit und auf kreative Einfälle.

Mitarbeiter Web App (max. 3 Personen pro Team)

Über eine praktische App, die auf Handy und Rechner laufen kann, soll man als Mitarbeiter einige typische organisatorische Aufgaben erledigen können. Ein paar Anregungen:

  • Urlaubsanträge

  • Krankschreibung

  • Passwortänderung

  • über aktuelle Events informieren

  • Andere Mitarbeiter finden

Werdet selber kreativ! Was würdet ihr euch in einer Mitarbeiter App noch wünschen?

Ein mögliches Favicon für die Web App

Geschenke Inspirator (max. 3 Personen pro Team)

Ein aus dem Alltag bekanntes Problem: Es ist schon wieder einer dieser Anlässe fällig und man hat noch kein Geschenk gefunden. Überlegt euch, wie ihr Kunden helfen könnt, ein Geschenk für ihre Liebsten zu finden.

Ein nicht so kreatives Geschenk.

Aufgaben mit hoher Komplexität

Bei diesen Aufgaben müsst ihr sehr schwere Probleme lösen, die aus der Sicht der Informatik NP-schwer sind. Eure Algorithmen müssen hier nur Instanzen einlesen können und eine valide Lösung dazu produzieren. Die Bewertung erfolgt anhand der Güte der Lösung und der Geschwindigkeit auf Testinstanzen, die den vorgegebenen ähnlich sind. Außerdem muss hier jede Abgabe mindestens eine Testinstanz testinstance.json enthalten, deren Lösung durch euren Ansatz besonders gut gelingt. (Diese Testinstanzen fließen in die Bewertung von allen Algorithmen ein, die abgegeben werden. Die Lösung darf nicht hard gecoded werden.)

Pro Instanz hat euer Projekt 100 Sekunden Zeit auf 16 GB RAM und 8 Prozessorkernen. Wenn ihr einen Timeout habt, bekommt ihr für die Instanz eine sehr schlechte Lösungsgüte angerechnet. Die beste durchschnittliche Lösungsgüte auf den Instanzen zu erreichen, führt zum besten Rating.

Von der Software und eurer testinstance.json wird erwartet, dass der Aufruf

docker run [[docker Argumente die ihr braucht]] random-image-tag [[Optionale Argumente]] testinstance.json

funktioniert und auf handelsüblicher Hardware in unter 100 Sekunden fertig ist und nur die Lösung auf die Konsole schreibt. Das Austauschen der testinstance.json durch eine korrekte andere Instanz soll höchstens dazu führen, dass eure Software länger als 100 Sekunden braucht.

Variante eines Capacitated Vehicle Routing Problem (max. 3 Person pro Team)

Wenn ein Kunde bei uns bestellt, ordnen wir die Bestellung in eine Liste (Pickpool) ein. Artikel, die in diesem Pickpool sind, müssen ihren Weg zum Depot finden. Unsere Kollegen im Lager sollen diesen Pickpool möglichst effizient abarbeiten. Jeder Artikel hat eine gewisse Masse und die Wagen, die für Touren im Lager benutzt werden, haben eine Maximalbeladung.

Auf https://developers.google.com/optimization/routing/cvrp könnt ihr euch zum Beispiel anlesen, wie man diese Probleme mit Google OR Tools löst. Damit könnt ihr wahrscheinlich gute Lösungen produzieren, aber mit der richtige Heuristik könnt ihr noch bessere Resultate erzielen. Die Instanzen, die wir zur Bewertung nutzen, haben einen gitterförmigen Graphen mit bis zu 10000 Knoten, 12000 Kanten und 100 Pickpooleinträge.

Gegeben: Ein zusammenhängender gewichteter Graph, ein Pickpool und eine Maximalbeladung.

{

"graph": {

"nodes": 5,

"edges":[[1,2,4],[1,3,4],[3,4,5],[1,5,1]]

},

"pickpool":[[3,5],[2,4],[4,3],[3,9]],

"cap": 10

}

Der Depot Knoten ist immer die 1.

Die Kante, die durch [1,2,4] beschrieben wird, ist eine Kante vom Knoten 1 zum Knoten 2 mit der Länge 4. Ein Eintrag des Pickpools wie [3,5] ist ein Gegenstand der auf dem Knoten 3 liegt und das Gewicht 5 hat. Bei allen Instanzen ist davon auszugehen, dass das Gewicht jedes Artikels kleiner als die Maximalbeladung ist.

Gesucht: Eine Menge von Touren, die im Depot starten und enden, nach deren Abarbeitung der Pickpool leer ist. Das Optimierungsziel ist es, die insgesamte Tourlänge zu minimieren.

[[2,0],[3],[1]]

Diese Rückgabe enthält die Indizes der Pickpool-Liste. Die Tour [2,0] startet im Depotknoten, besucht dann als erstes den Pickpooleintrag 2 ([4,3]), dann den Pickpooleintrag 0 ([3,5]) und kehrt dann wieder ins Depot zurück. Der kürzeste Weg vom Depot zum Knoten 4 hat die Länge 9. Vom Knoten 4 zum Knoten 3 kommt die Länge 5 hinzu und von dort zurück zum Depot kommen weitere 4 Längeneinheiten hinzu. Somit hat die Tour die Länge 18. Die Maximalbeladung des Wagens wird auch nicht überschritten, da die beiden Pickpool Einträge nur 3+5 = 8 wiegen.

Beispiel Instanz mit den Lösungs-Touren

3D Packing (max. 3 Person pro Team)

Kunden kaufen auch gerne mal mehrere Produkte auf einmal. Diese Bestellungen sollten idealerweise mit möglichst geringen Versandkosten versendet werden. Die zu packenden Produkte sind Quader und müssen in quaderförmige Pakete gepackt werden. Artikel und Pakete können nicht gedreht werden.

Mehrere Artikel können auch in ein einzelnes Paket gepackt werden, sofern sie sich nicht überschneiden. Instabile oder innerhalb eines Paketes "fliegende" Artikel sind erlaubt. Es wird nur getestet, ob die Artikel innerhalb des gewählten Pakets liegen und sich nicht überschneiden.

Gegeben: Eine Menge von möglichen Paketen mit den korrespondierenden Versandkosten und eine Menge von zu verschickenden Artikeln.

{

"package_types":[

{"dimensions":[10,20,15],"cost":10},

{"dimensions":[10,10,10],"cost":5}

],

"articles":[[10,10,5],[5,5,5],[9,4,5],[10,20,10],[10,10,10]]

}

In diesem Fall haben wir 2 Paketsorten zur Auswahl und müssen 5 Artikel versenden. Bei den Paketsorten ist in "dimensions" jeweils eine x, eine y und eine z-Ausprägung angegeben. Außerdem sind die Versandkosten als "cost" gegeben. Bei den Artikeln sind jeweils x, y und z-Ausprägungen angegeben, die in mindestens eine Paketsorte passen.

Gesucht: Aufteilung der Artikel auf die Pakete und Positionierung der Artikel innerhalb der Pakete. Das Optimierungsziel ist es, die insgesamten Versandkosten zu minimieren.

{

"used_packages":[0,1,1],

"article_positions":[[2,0,0,0],[2,5,1,5],[2,1,6,5],[0,0,0,0],[1,0,0,0]]

}

Diese Rückgabe bedeuted, dass 1 Pakete der Paketsorte mit Index 0
({"dimensions":[10,20,15],"cost":10}) und 2 Pakete der Paketsorte mit Index 1
({"dimensions":[10,10,10],"cost":5}) versendet werden.
Damit liegen die Kosten schonmal bei 10 + 5 + 5 = 20.

Danach werden die Positionen der 5 Artikel angegeben. Die Reihenfolge ist hier dieselbe wie in der Eingabe. Der erste Eintrag [2,0,0,0] bedeutet, dass im Paket mit Index 2 (aus den "used_packages") der Artikel mit den Dimensionen [10,10,5] an die Position (0,0,0) gelegt wird. Der zweite Eintrag [2,5,1,5] bedeutet, dass im Paket mit Index 2 (aus den "used_packages") der Artikel mit den Dimensionen [5,5,5] an die Position (5,1,5) gelegt wird. Der letzte Eintrag [1,0,0,0] bedeutet, dass im Paket mit Index 1 (aus den "used_packages") der Artikel mit den Dimensionen [10,10,10] an die Position (0,0,0) gelegt wird.

Paket 0 und 1 sind also Pakete, die jeweils nur einen Artikel enthalten. Eine Visualisierung von Paket 2 findet ihr neben dieser Aufgabe.

Lösung Paket 2

Spielereien für nebenbei

Diese Aufgaben haben weniger mit Relaxdays zu tun. Eine Grundfaszination für solche Aufgaben besteht aber bei vielen Kollegen.

Fubini Zahlen (max. 1 Person pro Team)

Erstelle ein Programm, das eine Liste der ersten n Fubini Zahlen berechnet. Eine Lookup Tabelle oder Ähnliches zu bauen, ist verboten.

Zur Eingabe n = 10 sollte beispielsweise die folgende Rückgabe kommen:

[1, 1, 3, 13, 75, 541, 4683, 47293, 545835, 7087261]

Je schneller euer Programm ist, desto besser fällt euer Rating aus.

Ein Aufruf wie

docker run [[docker Argumente die ihr braucht]] random-image-tag [[Optionale Argumente]] 10

sollte funktionieren.

Strenge schwache Ordnungen auf 3 Elementen

Meine eigene Kompression (max. 1 Person pro Team)

Implementiere eine Kompression für Dockerfiles und auch die dazugehörige Dekompression des Dockerfiles.

Die Kompression darf leicht verlustbehaftet sein. Das gebaute Image darf sich aber nicht unterscheiden. Das dekomprimierte Image wird mit dem unkomprimierten Image layerweise verglichen.

Wie das ungefähr passiert wird im Repo https://github.com/LukasKaufmannRelaxdays/hackathon-dockerfile-comparision gezeigt.

Je kleiner eure komprimierten Dockerfiles sind, desto besser fällt euer Rating aus.

Damit ihr die Files aus dem Dockercontainer zurückgeben könnt, mounten wir hier den Ordner /output an, in den alle eure Ausgabefiles geschrieben werden sollen.

Ein Aufruf wie

docker run [[docker Argumente die ihr braucht]] random-image-tag [[Optionale Argumente]] dockerfile-to-compress

sollte funktionieren und /output/dockerfile-to-compress.compressed erstellen.

Ebenso sollte ein Aufruf wie

docker run [[docker Argumente die ihr braucht]] random-image-tag [[Optionale Argumente]] dockerfile-to-compress.compressed

funktionieren und /output/dockerfile-to-compress.decompressed erstellen. Es empfiehlt sich beim Dekomprimieren in der Readme.md andere Argumente zu fordern. Ansonsten könnt ihr aber auch die Dateiendung verwenden, um zu entscheiden, ob komprimiert oder dekomprimiert wird.

Dockerfile Kompression

Aufgaben im Backend

Bei diesen Aufgaben müsst ihr ein API mit swagger Oberfläche bauen. Die Herausforderung besteht zum Einen darin, eine performante API zu schreiben und zum Anderen darin, dass euer Code erweiterbar geschrieben ist. Letzteres wird dadurch überprüft, dass sich die Aufgabe während der Hackathon läuft verändert und weitere Funktionalitäten fordert oder gar Änderungen an bestehenden Endpunkten nötig macht. Die Bewertung erfolgt über einen Performancetest, der alle Endpunkte aufruft (hauptsächlich die POST und GET Endpunkte). Wenn euer Code hier am schnellsten ist, bekommt ihr die meisten Punkte.

Planmäßig geschehen die Änderungen Freitag 20 Uhr, Samstag 12, 14 und 16 Uhr und Sonntag um 12 Uhr.

Projekte, bei denen nach den Änderungen ein grobes Überfliegen des Codes nicht mehr ausreicht, um ihn zu verstehen, werden nicht bewertet.

Lager (max. 1 Person pro Team)

Schreibe eine API, die CRUD Operationen für Lagerplätze implementiert.

Ein Lagerplatz hat einen identifizierenden Namen, eine ArticleID und einen Bestand. Das heißt in JSON Notation sieht ein Lagerplatz zum Beispiel so aus:

{

"name": "HAL-12;2;3;1",

"articleID": 10010020,

"bestand": 20

}

Update 1:
Die CRUD Endpunkte sollen umbenannt werden:

  • POST storagePlace(Lagerplatz) -> Um einen Lagerplatz hinzuzufügen
    curl -X 'POST' --data '{lagerplatz}' url/storagePlace

  • GET storagePlace(String x) -> Um den Lagerplatz mit dem Namen x abzurufen
    curl -X 'GET' url/storagePlace?x=HAL-12;2;3;1

  • PUT storagePlace(Lagerplatz) -> Um den Lagerplatz zu updaten. (Alter Lagerplatz wird über "name" gefunden)
    curl -X 'PUT' --data '{lagerplatz}' url/storagePlace

  • DELETE storagePlace(String x) -> Um den Lagerplatz mit dem Namen x zu löschen
    curl -X 'DELETE' url/storagePlace?x=HAL-12;2;3;1

Es soll weiterhin eine cursor-paginierter Endpunkt GET storagesPlaces(int n, string x) erstellt werden, der die n Lagerplätze zurückgibt, die lexikografisch nach x kommen. Dabei ist x ein optionaler Parameter. Wenn x nicht gegeben ist, werden die ersten n Lagerplätze der lexikografisch sortierten Lagerplatzliste zurückgegeben. Falls x selbst ein Name eines Lagerplatzes ist, soll der entsprechende Lagerplatz nicht mit zurückgegeben werden.
curl -X 'GET' url/storagePlaces?x=HAL-12;2;3;1&n=100

Update 2:
Die Lagerplatzbezeichnungen bauen sich aus 5 Teilen auf. "HAL-12;2;3;1" steht zum Beispiel dafür, dass der Standort des Lagerplatzes "HAL" ist und dass der Lagerplatz im Lagerabschnitt 12 in der zweiten Reihe am dritten Platz auf der Höhe 1 liegt. Das Attribut "name" soll daher in die 5 Attribute "standort", "lagerabschnitt", "reihe", "platz", "hoehe" aufgespalten werden, sodass Lagerplätze in JSON Notation zukünftig so aussehen:

{

"standort": "HAL",

"lagerabschnitt": 12,

"reihe": 2,

"platz": 3,

"hoehe": 1,

"articleID": 10010020,

"bestand": 20

}

Stellt euch aber vor, dass eure API bereits von ganz vielen Konsumenten aufgerufen wird. Ihr müsst daher eine Abwärtskompatibilität herstellen und die alten Endpunkte erhalten. Damit dies in Zukunft Strukturierter ablaufen kann baut ihr eine API Versionierung ein und erstellt die Endpunkte für die neue Spezifikation auf

  • POST storagePlace(Lagerplatz) -> Um einen neu spezifizierten Lagerplatz hinzuzufügen
    curl -X 'POST' --data '{lagerplatz}' url/v1/storagePlace

  • GET storagePlace(String x) -> Um den Lagerplatz mit dem Namen x abzurufen. Für den Request soll weiterhin die kompakte Schreibweise des Lagerplatznamens benutzt werden.
    curl -X 'GET' url/v1/storagePlace?x=HAL-12;2;3;1

  • PUT storagePlace(Lagerplatz) -> Um den Lagerplatz zu updaten. (Alter Lagerplatz wird über die 5 Attribute gefunden)
    curl -X 'PUT' --data '{lagerplatz}' url/v1/storagePlace

  • DELETE storagePlace(String x) -> Um den Lagerplatz mit dem Namen x zu löschen. Für den Request soll weiterhin die kompakte Schreibweise des Lagerplatznamens benutzt werden.
    curl -X 'DELETE' url/v1/storagePlace?x=HAL-12;2;3;1

  • GET storagePlaces(int n, string x) -> Siehe Update 1. Auch hier soll die kompakte Schreibweise des Lagerplatznamens weiter benutzt werden.
    curl -X 'GET' url/v1/storagePlaces?n=100&x=HAL-12;2;3;1

Update 3:

Alle Lagerplätze sollen jetzt das Attribut "kapazitaet" erhalten. Es gibt an wieviel Bestand des Artikels an dem Lagerplatz untergebracht werden kann. Bei Aufrufen an legacy Endpunkte soll diese den Wert vom Feld "bestand" annehmen. Folgende neuen Endpunkte sollen gebaut werden:

  • Alle Endpunkte der Version v1 sind mit dem Zusatzattribut "kapazitaet" in der Version v2 enthalten.

  • GET storagePlacesForArticleID(int x) -> Gibt alle Lagerplätze zurück, die den Artikel mit der ID x vorrätig haben.
    curl -X 'GET' url/v2/storagePlacesForArticleID?x=10010020

Außerdem soll ein Logging der Requests an alte api Versionen implementiert werden. Dazu schreibt ihr RequestPath, Timestamp, HTTP Verb, IP von der der Request kam und den Header X-Forwarded-For auf die Konsole.
DeprecatedCall@CC-VOL1: 10.0.2.100 13/Mar/2021:13:59:58 GET url/v1/storagePlace?x=HAL-12;2;3;1 X-Forwarded-For=127.0.0.1,0.0.0.0

In der Ausgabe eures Programmes wird ein grep nach DeprecatedCall@CC-VOL1: ausgeführt. Was danach bis zum Ende der Zeile steht, sollte im gegebenen Format sein.

Update 4:

Es sollen jetzt auch Requests an die Version v2 geloggt werden. Eine neue Version v3 soll ausgeliefert werden und eine Basic Authentication fordern. Nur noch Requests der folgenden Form sollen bei v3 erlaubt werden.

curl -u user:pass url/v3/...

Es soll genau einen User user mit dem Passwort pass geben.

Update 5:

Schreibe einen weiteren Cursor paginierten Endpunkt GET storagesPlacesAtLocation(string l, string x, int n), der die n Lagerplätze am Standort l zurückgibt, die lexikografisch nach x kommen. Dabei ist x ein optionaler Parameter. Wenn x nicht gegeben ist, werden die ersten n Lagerplätze der lexikografisch sortierten Lagerplatzliste am Standort l zurückgegeben. Falls x selbst ein Name eines Lagerplatzes ist, soll der entsprechende Lagerplatz nicht mit zurückgegeben werden.

curl -X 'GET' url/v3/storagePlacesAtLocation?l=HAL&x=HAL-12;2;3;1&n=100


Ein Kollege im Lager bei seiner Arbeit

Einkauf (max. 1 Person pro Team)

Schreibe eine API, die Einkäufe speichern und wiedergeben kann.

Es soll also ein POST Endpunkt geschaffen werden, der Einkaufsobjekte entgegennimmt und diese speichert und einen GET Endpunkt der alle abgespeicherten Einkäufe zurückgibt.

Ein Einkauf hat einen Lieferanten, eine ArticleID und einen Menge. Das heißt in JSON Notation sieht ein Einkauf zum Beispiel so aus:

{

"lieferant": "xyz productions ltd.",

"articleID": 10010020,

"menge": 20

}

Update 1:
Die Endpunkte sollen umbenannt werden:

  • POST purchase(Einkauf) -> Um einen Einkauf hinzuzufügen
    curl -X 'POST' --data '{einkauf}' url/purchase

  • GET purchases() -> Um alle Einkäufe abzurufen
    curl -X 'GET' url/purchases

Es soll weiterhin ein Endpunkt GET purchasesForArticle(int x) erstellt werden, der alle Einkäufe findet, die den Artikel mit der articleID x kaufen.
curl -X 'GET' url/purchasesForArticle?x=10010020

Update 2:

Der folgende neue Endpunkt soll hinzugefügt werden.

  • GET searchLieferant(String x) -> Findet eine Liste von Lieferanten, die zu dem Suchstring x eine Levenshtein-Distanz von maximal 10 haben und ordnet diese Liste nach der Levenshtein-Distanz. Als erstes sollen die Lieferanten mit der geringsten Levenshtein Distanz ausgegeben werden. Die Lieferanten sollen einfach als Liste von Strings ausgegeben werden (["lieferant1","lieferant2"]). Dopplungen sollten nicht auftreten.
    curl -X 'GET' url/searchLieferant?x="xyz prodcutoins"

Update 3:

Es soll möglich werden, alle Einkäufe zu returnen, die zwischen zwei Daten liegen. Dafür müsst ihr für jeden POST eines Einkaufs die Systemzeit speichern. Dann soll der folgende Endpunkt hinzugefügt werden:

  • GET purchasesBetween(String x, String y) -> Findet eine Liste von Einkäufen, die zwischen den Zeitpunkten x und y getätigt wurden. Einkäufe die exakt zum Zeitpunkt x oder y passiert sind, sind in der Ausgabe enthalten.
    curl -X 'GET' url/purchasesBetween?x="13.03.2021 13:59:58"&y="20.03.2021 15:59:58"

Update 4:

Nun soll auch der Preis mit in das Datenmodell aufgenommen werden. Passe alle Endpunkte entsprechend an.

{

"lieferant": "xyz productions ltd.",

"articleID": 10010020,

"menge": 20,

"preis": 4499.99

}

Der Preis ist für die Gesamtmenge und in Euro umgerechnet. Ein neuer Endpunkt GET plot(int x), der für den Artikel mit der articleID x, den Stückpreis über die Zeit visualisiert und als Bild im PNG Format zurückgibt, soll gebaut werden. Auf der x-Achse soll die Zeit und auf der y-Achse der Stückpreis stehen. Der Request wird nicht vom Performancetest ausgeführt. Ihr solltet mindestens 800x600 Pixel Grafiken generieren.

  • curl -X 'GET' url/plot?x=10010020

Update 5:

Der folgende neue Endpunkt soll hinzugefügt werden.

  • GET articlesForLieferant(string x) -> Gibt die Liste von "articleID"s zurück, die vom Lieferanten x eingekauft wurden. Die Liste wird nach dem zu letzt bezahlten Stückpreis aufsteigend sortiert. Artikel sollen sich in der Ausgabe nicht doppeln.
    curl -X 'GET' url/articlesForLieferant?x="xyz productions ltd."


Excel Tabelle eines Einkäufers