Compare commits
	
		
			17 commits
		
	
	
		
			fdd653c665
			...
			5ba83cfd07
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 5ba83cfd07 | ||
|  | 256e29a310 | ||
|   | 88b1b119df | ||
|   | f24b4dcbd1 | ||
|   | 08787e3ef7 | ||
|   | 4da25aac73 | ||
|   | b825a98d95 | ||
|   | b869b89836 | ||
|   | 4f12d941f9 | ||
|   | 492a726497 | ||
|   | 70d5f014e5 | ||
|  | 173ce00cf2 | ||
|  | a2202f311e | ||
|  | 184d0e538b | ||
|  | 82cbc4cf2c | ||
|  | 4956818d53 | ||
|  | 8cf2692f65 | 
|  | @ -1,5 +1,5 @@ | |||
| cmake_minimum_required(VERSION 3.28) | ||||
| project(itat_challange_olympics) | ||||
| project(itat_challenge_olympics) | ||||
| 
 | ||||
| find_package(Qt6 6.2 COMPONENTS Core Quick Quick REQUIRED) | ||||
| 
 | ||||
|  | @ -8,12 +8,12 @@ set(CMAKE_CXX_STANDARD 17) | |||
| set(CMAKE_AUTORCC ON) | ||||
| set(CMAKE_AUTOMOC ON) | ||||
| set(CMAKE_AUTOUIC ON) | ||||
| qt_add_executable(itat_challange_olympics src/main/main.cpp | ||||
| qt_add_executable(itat_challenge_olympics src/main/main.cpp | ||||
|         application.qrc | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| qt_add_qml_module(itat_challange_olympics | ||||
| qt_add_qml_module(itat_challenge_olympics | ||||
|     URI itat | ||||
|     QML_FILES | ||||
|         res/gui/application.qml | ||||
|  | @ -23,17 +23,13 @@ qt_add_qml_module(itat_challange_olympics | |||
|     SOURCES | ||||
|         src/model/Competitor.cpp | ||||
|         src/model/Competitor.h | ||||
|         src/model/CompetitorWithResults.cpp | ||||
|         src/model/CompetitorWithResults.h | ||||
|         src/model/EventInfo.cpp | ||||
|         src/model/EventInfo.h | ||||
|         src/model/FilterModel.cpp | ||||
|         src/model/FilterModel.h | ||||
|         src/model/MedalWinner.cpp | ||||
|         src/model/MedalWinner.h | ||||
|         src/model/Sport.cpp | ||||
|         src/model/Sport.h | ||||
|      | ||||
|         src/model/SportModel.cpp | ||||
|         src/model/SportModel.h | ||||
| 
 | ||||
|     RESOURCES | ||||
|         res/pictograms/ARC_small.svg | ||||
|         res/pictograms/ATH_small.svg | ||||
|  | @ -51,7 +47,9 @@ qt_add_qml_module(itat_challange_olympics | |||
|         res/pictograms/CSP_small.svg | ||||
|         res/pictograms/CTR_small.svg | ||||
|         res/pictograms/DIV_small.svg | ||||
|         res/pictograms/EQU_small.svg | ||||
|         res/pictograms/EDR_small.svg | ||||
|         res/pictograms/EJP_small.svg | ||||
|         res/pictograms/EVE_small.svg | ||||
|         res/pictograms/FBL_small.svg | ||||
|         res/pictograms/FEN_small.svg | ||||
|         res/pictograms/GAR_small.svg | ||||
|  | @ -81,8 +79,9 @@ qt_add_qml_module(itat_challange_olympics | |||
|         res/pictograms/WLF_small.svg | ||||
|         res/pictograms/WPO_small.svg | ||||
|         res/pictograms/WRE_small.svg | ||||
|         res/pictograms/WRG_small.svg | ||||
| ) | ||||
| 
 | ||||
| target_link_libraries(itat_challange_olympics PRIVATE Qt6::Core Qt6::Quick Qt6::Network) | ||||
| target_link_libraries(itat_challenge_olympics PRIVATE Qt6::Core Qt6::Quick Qt6::Network) | ||||
| 
 | ||||
| # target_link_libraries(itat_challange_olympics PRIVATE d3d12.lib dxgi.lib d3dcompiler.lib dxguid.lib) | ||||
| # target_link_libraries(itat_challenge_olympics PRIVATE d3d12.lib dxgi.lib d3dcompiler.lib dxguid.lib) | ||||
|  |  | |||
							
								
								
									
										170
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,170 @@ | |||
| # Olympia 2024 Events | ||||
| > View updated Events with its Competitors and Rankings of all Disciplines  | ||||
| 
 | ||||
| *Olympia 2024 Events* always displays up to date information of the 2024 | ||||
| Olympics in Paris. It achieves this by fetching the `olympics.com` API.  | ||||
| 
 | ||||
| We use the Model-View-Delegate pattern to synchronize the API data in C++ with | ||||
| the UI-Widgets defined in QML. For this we implement the API data as a Model | ||||
| which can be seen and interacted with by QML Components. | ||||
| 
 | ||||
| All code, qml definitions and images, etc are compiled into a single binary that | ||||
| is not dependent on any resources on relative paths anymore.  | ||||
| 
 | ||||
| On startup the default discipline *Archery* will be fetched and shown on the | ||||
| EventsPage. From here the user has three options. You can change the discipline | ||||
| from the Dropdown-Menu (Combobox) in the top left (also note the changing | ||||
| pictograms of the discipline); Filter the EventNames with the Search field in | ||||
| the top right; or click on an Event.  | ||||
| 
 | ||||
| When clicking on an Event, the user is redirected to the EventInfoPage. Here you | ||||
| can see Information about all Competitors that took part in the Event. When you | ||||
| are done, you can go back to the EventsPage with the button in the top left.  | ||||
| 
 | ||||
| ## Galery | ||||
| 
 | ||||
| <table> | ||||
| <tr> | ||||
| <td><img src="doc/events_page_combobox.png"/></td> | ||||
| <td><img src="doc/events_page_textfield.png"/></td> | ||||
| <td><img src="doc/event_info.png"/></td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td>Select discipline</td> | ||||
| <td>Filter by Event</td> | ||||
| <td>View Competitor</td> | ||||
| </tr> | ||||
| </table> | ||||
| 
 | ||||
| ## Getting Started | ||||
| 
 | ||||
| ### Dependencies | ||||
| 
 | ||||
| - Qt6 | ||||
| 
 | ||||
| ### Installation | ||||
| 
 | ||||
| ```sh | ||||
| git clone git@gitlab.kit.edu:ugmgt/itat_challenge_2024.git | ||||
| # or download release | ||||
| cd itat_challenge_2024 | ||||
| cmake -B build | ||||
| cmake --build build | ||||
| ./build/itat_challenge_olympics | ||||
| ``` | ||||
| 
 | ||||
| ## Code Structure | ||||
| 
 | ||||
| ### UML Diagram | ||||
| 
 | ||||
| ```plantuml | ||||
| @startuml | ||||
| allowmixing | ||||
| set namespaceSeparator none | ||||
| skinparam ranksep 10  | ||||
| 
 | ||||
| package C++ <<Frame>> { | ||||
|     class Application { | ||||
|         QGuiApplication app | ||||
|             QmlComponent component | ||||
|             SportModel model | ||||
|             FilterModel<SportModel> filter | ||||
|     } | ||||
| 
 | ||||
|     class SportModel { | ||||
|         String discipline | ||||
|             <EventInfo> model | ||||
|             request(String discipline) | ||||
|             parseData() | ||||
|     } | ||||
| 
 | ||||
|     class FilterModel { | ||||
|         void setFilterFixedString(String) | ||||
|     } | ||||
| 
 | ||||
|     class EventInfo { | ||||
|         String eventName | ||||
|             List<Competitor> competitors | ||||
|     } | ||||
| 
 | ||||
|     class Competitor { | ||||
|         String name | ||||
|             String code | ||||
|             String noc | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| package QML <<Frame>> { | ||||
| 
 | ||||
|     component EventInfoPage { | ||||
|         component [Page] as EIPage { | ||||
|             component [ToolBar] as EIToolBar | ||||
|             component [ListView] as EILisView | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     EIToolBar -[hidden]- EILisView | ||||
| 
 | ||||
|     component EventsPage { | ||||
|         component [Page] as EPage { | ||||
|             component [ToolBar] as EToolBar | ||||
|             component [Column] as EColumn { | ||||
|                 component [Row] as ERow { | ||||
|                     component [ComboBox] as EComboBox | ||||
|                     component [TextField] as ETextField | ||||
|                 } | ||||
|                 component [ListView] as EListView | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     EToolBar -[hidden]- EColumn | ||||
|     ERow -[hidden]- EListView | ||||
| 
 | ||||
|     component application.qml { | ||||
|         component ApplicationWindow { | ||||
|             component StackView | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 'application.qml -u- a | ||||
| 'Application -r- a | ||||
| 
 | ||||
| Application *-- "1" SportModel | ||||
| Application *-- "1" FilterModel | ||||
| 
 | ||||
| FilterModel "1" o-- "1" SportModel | ||||
| 
 | ||||
| SportModel *-- "0..*" EventInfo | ||||
| EventInfo *-- "0..*" Competitor | ||||
| 
 | ||||
| Application <.l. application.qml | ||||
| StackView <.. EventInfoPage | ||||
| StackView <.. EventsPage | ||||
| 
 | ||||
| EComboBox "request()" .> SportModel | ||||
| 
 | ||||
| EComboBox -[hidden]u- ETextField | ||||
| 
 | ||||
| SportModel "View" .> EListView  | ||||
| FilterModel "View" .> EListView  | ||||
| ETextField "Control" .r.> FilterModel | ||||
| 
 | ||||
| EILisView <. "View" Competitor | ||||
| 
 | ||||
| cloud api.olympics.com | ||||
| () REST | ||||
| REST - api.olympics.com | ||||
| 
 | ||||
| SportModel -( REST | ||||
| 
 | ||||
| application.qml -[hidden]u- Application | ||||
| @enduml | ||||
| ``` | ||||
| 
 | ||||
| ## Authors | ||||
| 
 | ||||
| - **Silas Stulz** (ugmgt, 2468197) - *Initial Work* | ||||
| - **Gero Beckmann** (ukpfm, 2409754) - *Initial Work* | ||||
| 
 | ||||
							
								
								
									
										
											BIN
										
									
								
								doc/event_info.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 19 KiB | 
							
								
								
									
										
											BIN
										
									
								
								doc/events_page_combobox.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 49 KiB | 
							
								
								
									
										
											BIN
										
									
								
								doc/events_page_textfield.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 40 KiB | 
|  | @ -4,8 +4,8 @@ import QtQuick.Controls | |||
| 
 | ||||
| Page { | ||||
|   id: root | ||||
|   property string eventName | ||||
|   property list<string> competitors | ||||
|   required property string eventName | ||||
|   required property list<QtObject> competitors | ||||
| 
 | ||||
|   header: ToolBar { | ||||
|     ToolButton { | ||||
|  | @ -33,15 +33,25 @@ Page { | |||
|     spacing: 20 | ||||
|     model: competitors | ||||
|     delegate: ItemDelegate { | ||||
|       text: modelData | ||||
|       required property string name | ||||
|       required property string noc | ||||
|       required property string mark | ||||
|       required property string statistic | ||||
|       required property string gold | ||||
|       required property string silver | ||||
|       required property string bronze | ||||
|       width: listView.width - listView.leftMargin - listView.rightMargin | ||||
|       height: avatar.implicitHeight + 32 | ||||
|       leftPadding: avatar.implicitWidth + 32 | ||||
| 
 | ||||
|       Image { | ||||
|         id: avatar | ||||
|         // source: "images/" + modelData.replace(" ", "_") + ".png" | ||||
|       height: 32 | ||||
|       Text {  | ||||
|         anchors.left: parent.left | ||||
|         text: name + " (" + noc + ")" | ||||
|       } | ||||
|       Text {  | ||||
|         anchors.right: parent.right | ||||
|         horizontalAlignment: Text.AlignRight | ||||
|         text: mark + " " + statistic + " | " + gold + "🥇 " + silver + "🥈 " + bronze + "🥉" | ||||
|       } | ||||
| 
 | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -254,7 +254,7 @@ Page { | |||
|       model: filter | ||||
|       delegate: ItemDelegate { | ||||
|         required property string eventName | ||||
|         required property list<string> competitors | ||||
|         required property list<QtObject> competitors | ||||
|         text: eventName | ||||
|         width: listView.width - listView.leftMargin - listView.rightMargin | ||||
|         height: avatar.height  | ||||
|  |  | |||
| Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										28
									
								
								res/pictograms/EJP_small.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,28 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 720 720"> | ||||
|   <defs> | ||||
|     <style> | ||||
|       .cls-1 { | ||||
|         clip-path: url(#clippath); | ||||
|       } | ||||
| 
 | ||||
|       .cls-2 { | ||||
|         fill: none; | ||||
|       } | ||||
| 
 | ||||
|       .cls-2, .cls-3 { | ||||
|         stroke-width: 0px; | ||||
|       } | ||||
| 
 | ||||
|       .cls-3 { | ||||
|         fill: #000; | ||||
|       } | ||||
|     </style> | ||||
|     <clipPath id="clippath"> | ||||
|       <rect class="cls-2" width="720" height="720"/> | ||||
|     </clipPath> | ||||
|   </defs> | ||||
|   <g class="cls-1"> | ||||
|     <path class="cls-3" d="M0,149.7C0,99.6,31.6,57,75.9,40.5L116.4,0v33.3l99.8,49.9-16.6,33.3h-83.2v149.7c-64.3-.1-116.4-52.2-116.4-116.5ZM215.4,216c30.4-30.4,79.6-30.4,110,0l5.9,5.9,28.7-28.7,11.3,11.3-68,68,35.6,45.1-24.6,24.6-57.4-23.2-24.2,24.2-17.2-17.2c-30.5-30.3-30.5-79.6-.1-110ZM269.2,306.7l41.3,16.8,7-7-25.6-32.5-22.7,22.7ZM603.6,116.4v149.7c64.3,0,116.4-52.1,116.4-116.4s-31.6-92.7-75.9-109.2L603.6,0v33.3l-99.8,49.9,16.6,33.3h83.2ZM603.6,453.9v149.7h-83.2l-16.6,33.3,99.8,49.9v33.2l40.5-40.5c44.3-16.5,75.9-59.1,75.9-109.2s-52.1-116.4-116.4-116.4ZM116.4,603.6v-149.7c-64.3,0-116.4,52.1-116.4,116.4s31.6,92.7,75.9,109.2l40.5,40.5v-33.3l99.8-49.9-16.6-33.3h-83.2ZM490,237.1l64-59c2-1.8,3.3-4.1,3.7-6.2s0-4.2-1.4-5.6l-1.3-1.3-1.3-1.3c-1.4-1.4-3.4-1.8-5.6-1.4s-4.4,1.7-6.2,3.7l-59,64-252.9,252.9-64,59c-2,1.8-3.3,4.1-3.7,6.2s0,4.2,1.4,5.6l1.3,1.3,1.3,1.3c1.4,1.4,3.4,1.8,5.6,1.4s4.4-1.7,6.2-3.7l59-64,252.9-252.9ZM504.6,504c-30.4,30.4-79.6,30.4-110,0l-5.9-5.9-28.7,28.6-11.3-11.3,68-68-35.6-45.1,24.6-24.6,57.4,23.3,24.2-24.2,17.2,17.2c30.5,30.3,30.5,79.6,0,110ZM450.8,413.3l-41.3-16.8-7,7,25.6,32.5,22.7-22.7Z"/> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										28
									
								
								res/pictograms/EVE_small.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,28 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 720 720"> | ||||
|   <defs> | ||||
|     <style> | ||||
|       .cls-1 { | ||||
|         clip-path: url(#clippath); | ||||
|       } | ||||
| 
 | ||||
|       .cls-2 { | ||||
|         fill: none; | ||||
|       } | ||||
| 
 | ||||
|       .cls-2, .cls-3 { | ||||
|         stroke-width: 0px; | ||||
|       } | ||||
| 
 | ||||
|       .cls-3 { | ||||
|         fill: #000; | ||||
|       } | ||||
|     </style> | ||||
|     <clipPath id="clippath"> | ||||
|       <rect class="cls-2" width="720" height="720"/> | ||||
|     </clipPath> | ||||
|   </defs> | ||||
|   <g class="cls-1"> | ||||
|     <path class="cls-3" d="M0,149.7C0,99.6,31.6,57,75.9,40.5L116.4,0v33.3l99.8,49.9-16.6,33.3h-83.2v149.7c-64.3-.1-116.4-52.2-116.4-116.5ZM215.4,216c30.4-30.4,79.6-30.4,110,0l5.9,5.9,28.7-28.7,11.3,11.3-68,68,35.6,45.1-24.6,24.6-57.4-23.2-24.2,24.2-17.2-17.2c-30.5-30.3-30.5-79.6-.1-110ZM269.2,306.7l41.3,16.8,7-7-25.6-32.5-22.7,22.7ZM603.6,116.4v149.7c64.3,0,116.4-52.1,116.4-116.4s-31.6-92.7-75.9-109.2L603.6,0v33.3l-99.8,49.9,16.6,33.3h83.2ZM603.6,453.9v149.7h-83.2l-16.6,33.3,99.8,49.9v33.2l40.5-40.5c44.3-16.5,75.9-59.1,75.9-109.2s-52.1-116.4-116.4-116.4ZM116.4,603.6v-149.7c-64.3,0-116.4,52.1-116.4,116.4s31.6,92.7,75.9,109.2l40.5,40.5v-33.3l99.8-49.9-16.6-33.3h-83.2ZM490,237.1l64-59c2-1.8,3.3-4.1,3.7-6.2s0-4.2-1.4-5.6l-1.3-1.3-1.3-1.3c-1.4-1.4-3.4-1.8-5.6-1.4s-4.4,1.7-6.2,3.7l-59,64-252.9,252.9-64,59c-2,1.8-3.3,4.1-3.7,6.2s0,4.2,1.4,5.6l1.3,1.3,1.3,1.3c1.4,1.4,3.4,1.8,5.6,1.4s4.4-1.7,6.2-3.7l59-64,252.9-252.9ZM504.6,504c-30.4,30.4-79.6,30.4-110,0l-5.9-5.9-28.7,28.6-11.3-11.3,68-68-35.6-45.1,24.6-24.6,57.4,23.3,24.2-24.2,17.2,17.2c30.5,30.3,30.5,79.6,0,110ZM450.8,413.3l-41.3-16.8-7,7,25.6,32.5,22.7-22.7Z"/> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										28
									
								
								res/pictograms/WRG_small.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,28 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 720 720"> | ||||
|   <defs> | ||||
|     <style> | ||||
|       .cls-1 { | ||||
|         clip-path: url(#clippath); | ||||
|       } | ||||
| 
 | ||||
|       .cls-2 { | ||||
|         fill: none; | ||||
|       } | ||||
| 
 | ||||
|       .cls-2, .cls-3 { | ||||
|         stroke-width: 0px; | ||||
|       } | ||||
| 
 | ||||
|       .cls-3 { | ||||
|         fill: #000; | ||||
|       } | ||||
|     </style> | ||||
|     <clipPath id="clippath"> | ||||
|       <rect class="cls-2" width="720" height="720"/> | ||||
|     </clipPath> | ||||
|   </defs> | ||||
|   <g class="cls-1"> | ||||
|     <path class="cls-3" d="M492.6,467.2c39.5-48.8,48.1-114.2,25.8-170.2,10.9-10.3,14-15.4,18.5-25.8.4-.8.7-1.7,1.1-2.6,1-2.2,3.5-5,7.3-8.1h0c40.1,74.6,32,168.4-24.4,235.2l-28.4-28.5ZM486.2,147.4l90.7-58.9,19.5,19.5-23,23c-2.1,2.1-3.2,4.9-3.2,7.8s1.2,5.7,3.2,7.8c2.1,2.1,4.9,3.2,7.8,3.2s5.7-1.2,7.8-3.2l23-23.1,19.5,19.5-58.9,90.7-2.8,1.2c-20.7,8.8-36.8,20.3-40.8,29.4-.4,1-.8,1.8-1.2,2.7-4.9,11.1-6.9,14.7-25.8,31.2-13.5,11.8-28.7,23.7-33.3,26.7l-9.7,6.4-4.8-10.6c-6-13.1-12.7-23-21.3-31.5l-2.3-2.3c-8.4-8.5-18.4-15.2-31.4-21.2l-10.6-4.8,6.4-9.7c3-4.5,14.9-19.7,26.7-33.3,16.5-18.8,20.1-20.9,31.2-25.8.8-.4,1.7-.8,2.7-1.2,9.1-4.1,20.6-20.1,29.4-40.8l1.2-2.7ZM435.3,277.6l13.6-13.6c-10-9.3-21.3-16.9-33.7-22.7-5.9,7.3-10.3,13.2-11.8,15.4,13.3,6.1,23.5,12.9,31.9,20.9ZM478.7,304.8c-5.8-12.4-13.4-23.7-22.7-33.7l-13.6,13.6c8,8.4,14.8,18.6,20.9,31.9,2.2-1.5,8.2-6,15.4-11.8ZM494.2,154.1c-6.5,15.3-19.3,39.3-34.5,46.1-11.9,5.3-13.3,4.9-30.4,24.4-1.2,1.3-2.3,2.7-3.5,4,12.6,6.3,24.2,14.4,34.5,24.1l26.4-26.4,7.1,7.1-26.4,26.4c9.7,10.3,17.7,21.9,24.1,34.5,1.4-1.2,2.7-2.3,4-3.5,19.5-17.1,19.1-18.5,24.4-30.4,6.8-15.2,30.7-28,46.1-34.5l52.8-81.3-6.7-6.7-16,16c-4.1,4.1-9.5,6.2-14.9,6.2s-10.8-2.1-14.9-6.2c-8.2-8.2-8.2-21.6,0-29.8l16-16-6.7-6.7-81.4,52.7ZM448.9,183c.8-.4,1.7-.7,2.6-1.1,2.2-1,5-3.5,8.1-7.3h0c-74.6-40.2-168.3-32.1-235.2,24.4l28.4,28.4c48.8-39.5,114.2-48.1,170.2-25.8,10.4-10.9,15.5-14,25.9-18.6ZM720,360l-131.8-131.8-8.9,13.6h0l118.2,118.2-156,156,11.3,11.3,167.3-167.3ZM0,360l131.8,131.8,8.9-13.6h.1l-118.2-118.2,156-156-11.3-11.3L0,360ZM233.8,572.6l-90.7,58.9-19.5-19.5,23-23c2.1-2.1,3.2-4.9,3.2-7.8s-1.2-5.7-3.2-7.8c-2.1-2.1-4.9-3.2-7.8-3.2s-5.7,1.1-7.8,3.2l-23,23.1-19.5-19.5,58.9-90.7,2.8-1.2c20.7-8.8,36.8-20.3,40.8-29.4.4-1,.8-1.8,1.2-2.7,4.9-11.1,6.9-14.7,25.8-31.2,13.5-11.8,28.7-23.7,33.3-26.7l9.7-6.4,4.8,10.6c6,13.1,12.7,23,21.3,31.5l2.3,2.3c8.4,8.5,18.4,15.2,31.4,21.2l10.6,4.8-6.4,9.7c-3,4.5-14.9,19.7-26.7,33.3-16.5,18.8-20.1,20.9-31.2,25.8-.8.4-1.7.8-2.7,1.2-9.1,4.1-20.6,20.1-29.4,40.8l-1.2,2.7ZM284.7,442.4l-13.7,13.7c10,9.3,21.3,16.9,33.7,22.7,5.9-7.3,10.3-13.2,11.8-15.4-13.2-6.2-23.4-13-31.8-21ZM241.3,415.2c5.8,12.4,13.4,23.7,22.7,33.7l13.6-13.6c-8-8.4-14.8-18.6-20.9-31.9-2.2,1.5-8.2,6-15.4,11.8ZM225.8,565.9c6.5-15.3,19.3-39.3,34.5-46.1,11.9-5.3,13.3-4.9,30.4-24.4,1.2-1.3,2.3-2.7,3.5-4-12.6-6.3-24.2-14.4-34.5-24.1l-26.4,26.4-7.1-7.1,26.4-26.4c-9.7-10.3-17.7-21.9-24.1-34.5-1.4,1.2-2.7,2.3-4,3.5-19.5,17.1-19.1,18.5-24.4,30.4-6.8,15.2-30.7,28-46.1,34.5l-52.8,81.3,6.7,6.7,16-16c4.1-4.1,9.5-6.2,14.9-6.2s10.8,2.1,14.9,6.2c8.2,8.2,8.2,21.6,0,29.8l-16,16,6.7,6.7,81.4-52.7ZM360,697.4l-118.1-118.1h0c0,0-13.6,9-13.6,9l131.7,131.7,167.3-167.3-11.3-11.3-156,156ZM360,22.6l118.1,118.1h0c0-.1,13.6-9,13.6-9L360,0l-167.3,167.3,11.3,11.3L360,22.6ZM199,224.4c-56.4,66.9-64.6,160.6-24.4,235.2h.1c3.8-3.1,6.3-5.9,7.3-8.1.4-.9.8-1.8,1.1-2.6,4.6-10.4,7.6-15.5,18.5-25.8-22.3-56-13.7-121.5,25.8-170.2l-28.4-28.5ZM271.1,537c-.8.4-1.7.7-2.6,1.1-2.2,1-5,3.5-8.1,7.3h0c74.6,40.2,168.3,32.1,235.2-24.3l-28.4-28.4c-48.7,39.5-114.2,48.1-170.2,25.8-10.4,10.8-15.5,13.9-25.9,18.5ZM391.1,379.8l262.6,262.6-11.3,11.3-262.6-262.6c-14.3,9.1-33.4,7.5-45.9-5s-14.1-31.6-5-45.9L66.3,77.7l11.3-11.3,262.6,262.6c14.3-9.1,33.4-7.5,45.9,5s14.1,31.5,5,45.8ZM347.5,336.2l36.3,36.3c5.3-10.1,3.7-23-4.8-31.5s-21.3-10.1-31.5-4.8ZM372.5,383.8l-36.3-36.3c-5.3,10.1-3.7,23,4.8,31.5s21.3,10.1,31.5,4.8Z"/> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 3.9 KiB | 
|  | @ -1,140 +0,0 @@ | |||
| 
 | ||||
| #include "OlympicsAPI.h" | ||||
| #include <string> | ||||
| #include <stdexcept> | ||||
| 
 | ||||
| // networking
 | ||||
| #include <QEventLoop> | ||||
| #include <QNetworkAccessManager> | ||||
| #include <QNetworkRequest> | ||||
| #include <QNetworkReply> | ||||
| #include <QUrl> | ||||
| #include <QUrlQuery> | ||||
| 
 | ||||
| // json parsing
 | ||||
| #include <QFile> | ||||
| #include <QJsonValue> | ||||
| #include <QJsonDocument> | ||||
| #include <QJsonObject> | ||||
| #include <QVariantMap> | ||||
| #include <QJsonArray> | ||||
| 
 | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief OlympicsAPI::getSportData Requests the current data from the Olympics API of a certain discipline. | ||||
|  * @param sport The discipline to request. | ||||
|  * @return The current information provided by the API endpoint. | ||||
|  */ | ||||
| QJsonObject OlympicsAPI::getSportData(OlympicsAPI::Disciplines sport) { | ||||
|     string shortName = this->getDisciplineShort(sport); | ||||
| 
 | ||||
|     if (USE_API_REQUEST) { | ||||
|         // create custom temporary event loop on stack
 | ||||
|         QEventLoop eventLoop; | ||||
| 
 | ||||
|         // "quit()" the event-loop, when the network request "finished()"
 | ||||
|         QNetworkAccessManager mgr; | ||||
|         QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit())); | ||||
| 
 | ||||
|         QString endpoint = (API_LINK + shortName).c_str(); | ||||
| 
 | ||||
|         // the HTTP request
 | ||||
|         QNetworkRequest req( (QUrl( endpoint )) ); | ||||
|         QNetworkReply *reply = mgr.get(req); | ||||
|         eventLoop.exec(); // blocks stack until "finished()" has been called
 | ||||
| 
 | ||||
|         if (reply->error() == QNetworkReply::NoError) { | ||||
|             //success
 | ||||
| 
 | ||||
|             QString strReply = (QString)reply->readAll(); | ||||
| 
 | ||||
|             //parse json
 | ||||
|             QJsonDocument jsonResponse = QJsonDocument::fromJson(strReply.toUtf8()); | ||||
| 
 | ||||
|             QJsonObject jsonObj = jsonResponse.object(); | ||||
| 
 | ||||
|             delete reply; | ||||
| 
 | ||||
|             return jsonObj; | ||||
|         } | ||||
|         else { | ||||
|             //failure
 | ||||
|             delete reply; | ||||
| 
 | ||||
|             throw invalid_argument("API request failed."); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // if API is not used, open locally stored data
 | ||||
|     QString filePath = ("../../res/mock/" + shortName + ".json").c_str(); | ||||
|     QFile file( filePath ); | ||||
| 
 | ||||
|     if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) { | ||||
|         throw invalid_argument("Could not open file to read data of the given discipline."); | ||||
|     } else { | ||||
|         QString text = file.readAll(); | ||||
|         file.close(); | ||||
|         return (QJsonDocument::fromJson(text.toUtf8())).object(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief OlympicsAPI::getDisciplineShort Get the discipline's short name defined by the IOC (International Olympic Committee) | ||||
|  * @param sport The sport you want to get the name from. | ||||
|  * @return The short name as a string. | ||||
|  */ | ||||
| string OlympicsAPI::getDisciplineShort(OlympicsAPI::Disciplines sport) { | ||||
|     switch (sport) { | ||||
|     case OlympicsAPI::Disciplines::AquaticsArtisticSwimming: return "SWA"; | ||||
|     case OlympicsAPI::Disciplines::AquaticsDiving: return "DIV"; | ||||
|     case OlympicsAPI::Disciplines::AquaticsMarathonSwimming: return "OWS"; | ||||
|     case OlympicsAPI::Disciplines::AquaticsSwimming: return "SWM"; | ||||
|     case OlympicsAPI::Disciplines::AquaticsWaterPolo: return "WPO"; | ||||
|     case OlympicsAPI::Disciplines::Archery: return "ARC"; | ||||
|     case OlympicsAPI::Disciplines::Athletics: return "ATH"; | ||||
|     case OlympicsAPI::Disciplines::Badminton: return "BDM"; | ||||
|     case OlympicsAPI::Disciplines::Basketball3v3: return "BK3"; | ||||
|     case OlympicsAPI::Disciplines::Basketball: return "BKB"; | ||||
|     case OlympicsAPI::Disciplines::Boxing: return "BOX"; | ||||
|     case OlympicsAPI::Disciplines::Breaking: return "BKG"; | ||||
|     case OlympicsAPI::Disciplines::CanoeingSprint: return "CSP"; | ||||
|     case OlympicsAPI::Disciplines::CanoeingSlalom: return "CSL"; | ||||
|     case OlympicsAPI::Disciplines::CyclingBMXFreestyle: return "BMF"; | ||||
|     case OlympicsAPI::Disciplines::CyclingBMXRacing: return "BMX"; | ||||
|     case OlympicsAPI::Disciplines::CyclingMaountainBike: return "MTB"; | ||||
|     case OlympicsAPI::Disciplines::CyclingRoad: return "CRD"; | ||||
|     case OlympicsAPI::Disciplines::CyclingTrack: return "CTR"; | ||||
|     case OlympicsAPI::Disciplines::EquestrianDressage: return "EDR"; | ||||
|     case OlympicsAPI::Disciplines::EquestrianEventing: return "EVE"; | ||||
|     case OlympicsAPI::Disciplines::EquestrianJumping: return "EJP"; | ||||
|     case OlympicsAPI::Disciplines::Fencing: return "FEN"; | ||||
|     case OlympicsAPI::Disciplines::FieldHockey: return "HOC"; | ||||
|     case OlympicsAPI::Disciplines::Football: return "FBL"; | ||||
|     case OlympicsAPI::Disciplines::Golf: return "GLF"; | ||||
|     case OlympicsAPI::Disciplines::GymnasticsArtistic: return "GAR"; | ||||
|     case OlympicsAPI::Disciplines::GymnasticsRhythmic: return "GRY"; | ||||
|     case OlympicsAPI::Disciplines::GymnasticsTrampoline: return "GTR"; | ||||
|     case OlympicsAPI::Disciplines::HandballIndoor: return "HBL"; | ||||
|     case OlympicsAPI::Disciplines::Judo: return "JUD"; | ||||
|     case OlympicsAPI::Disciplines::ModernPentathlon: return "MPN"; | ||||
|     case OlympicsAPI::Disciplines::Rowing: return "ROW"; | ||||
|     case OlympicsAPI::Disciplines::Rugby7: return "RU7"; | ||||
|     case OlympicsAPI::Disciplines::Sailing: return "SAL"; | ||||
|     case OlympicsAPI::Disciplines::Shooting: return "SHO"; | ||||
|     case OlympicsAPI::Disciplines::Skateboarding: return "SKB"; | ||||
|     case OlympicsAPI::Disciplines::SportClimbing: return "CLB"; | ||||
|     case OlympicsAPI::Disciplines::Surfing: return "SRF"; | ||||
|     case OlympicsAPI::Disciplines::TableTennis: return "TTE"; | ||||
|     case OlympicsAPI::Disciplines::Taekwondo: return "TKW"; | ||||
|     case OlympicsAPI::Disciplines::Tennis: return "TEN"; | ||||
|     case OlympicsAPI::Disciplines::Triathlon: return "TRI"; | ||||
|     case OlympicsAPI::Disciplines::VolleyballBeach: return "VBV"; | ||||
|     case OlympicsAPI::Disciplines::VolleyballIndoor: return "VVO"; | ||||
|     case OlympicsAPI::Disciplines::Weightlifting: return "WLF"; | ||||
|     case OlympicsAPI::Disciplines::WrestlingFreestyle: return "WRE"; | ||||
|     case OlympicsAPI::Disciplines::WrestlingGrecoRoman: return "WRG"; | ||||
|     default: return "ARC"; // default, which should not be possible, because of enum
 | ||||
|     } | ||||
| } | ||||
|  | @ -1,77 +0,0 @@ | |||
| 
 | ||||
| #ifndef ITAT_CHALLANGE_OLYMPICS_OLYMPICSAPI_H | ||||
| #define ITAT_CHALLANGE_OLYMPICS_OLYMPICSAPI_H | ||||
| 
 | ||||
| 
 | ||||
| #define API_LINK "https://sph-s-api.olympics.com/summer/schedules/api/ENG/schedule/discipline/"
 | ||||
| 
 | ||||
| #include <string> | ||||
| #include <QJsonObject> | ||||
| 
 | ||||
| // TODO: change this to true to use the olympics api, instead of the mock date in res/mock/
 | ||||
| #define USE_API_REQUEST false | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| class OlympicsAPI { | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     enum Disciplines { | ||||
|         AquaticsArtisticSwimming, | ||||
|         AquaticsDiving, | ||||
|         AquaticsMarathonSwimming, | ||||
|         AquaticsSwimming, | ||||
|         AquaticsWaterPolo, | ||||
|         Archery, | ||||
|         Athletics, | ||||
|         Badminton, | ||||
|         Basketball3v3, | ||||
|         Basketball, | ||||
|         Boxing, | ||||
|         Breaking, | ||||
|         CanoeingSprint, | ||||
|         CanoeingSlalom, | ||||
|         CyclingBMXFreestyle, | ||||
|         CyclingBMXRacing, | ||||
|         CyclingMaountainBike, | ||||
|         CyclingRoad, | ||||
|         CyclingTrack, | ||||
|         EquestrianDressage, | ||||
|         EquestrianEventing, | ||||
|         EquestrianJumping, | ||||
|         Fencing, | ||||
|         FieldHockey, | ||||
|         Football, | ||||
|         Golf, | ||||
|         GymnasticsArtistic, | ||||
|         GymnasticsRhythmic, | ||||
|         GymnasticsTrampoline, | ||||
|         HandballIndoor, | ||||
|         Judo, | ||||
|         ModernPentathlon, | ||||
|         Rowing, | ||||
|         Rugby7, | ||||
|         Sailing, | ||||
|         Shooting, | ||||
|         Skateboarding, | ||||
|         SportClimbing, | ||||
|         Surfing, | ||||
|         TableTennis, | ||||
|         Taekwondo, | ||||
|         Tennis, | ||||
|         Triathlon, | ||||
|         VolleyballBeach, | ||||
|         VolleyballIndoor, | ||||
|         Weightlifting, | ||||
|         WrestlingFreestyle, | ||||
|         WrestlingGrecoRoman | ||||
|     }; | ||||
| 
 | ||||
|     QJsonObject getSportData(Disciplines sport); | ||||
|     string getDisciplineShort(Disciplines sport); | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #endif //ITAT_CHALLANGE_OLYMPICS_OLYMPICSAPI_H
 | ||||
|  | @ -20,8 +20,8 @@ | |||
| // console output
 | ||||
| #include <QDebug> | ||||
| // #include <iostream>
 | ||||
| #include "../model/Sport.h" | ||||
| #include "../model/FilterModel.h" | ||||
| #include "../model/SportModel.h" | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|  |  | |||
|  | @ -1,15 +1,126 @@ | |||
| 
 | ||||
| #include "Competitor.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * Reads certain properties from a competitor json object. | ||||
|  * These are: code, name, noc, results | ||||
|  * | ||||
|  * For further information on 'results' see [Competitor::setResult]. | ||||
|  * | ||||
|  * Does not set the amounts of medals. For this, call [Competitor::setMedals]. | ||||
|  * | ||||
|  * @param competitor The competitor as a QJsonObject. | ||||
|  * @return True if successful. | ||||
|  */ | ||||
| bool Competitor::setCompetitor(const QJsonObject &competitor) { | ||||
|     if (!competitor.contains("code") | ||||
|         || !competitor.contains("name") | ||||
|         || !competitor.contains("noc")) { | ||||
|         throw invalid_argument("Not a competitor object."); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     this->code = competitor["code"].toString(); | ||||
|     this->name = competitor["name"].toString(); | ||||
|     this->noc = competitor["noc"].toString(); | ||||
|     setCode(competitor["code"].toInt()); | ||||
|     setName(competitor["name"].toString()); | ||||
|     setNOC(competitor["noc"].toString()); | ||||
| 
 | ||||
|     if (!competitor.contains("results")) return false; | ||||
|     QJsonObject results = competitor["results"].toObject(); | ||||
|     return setResults(results); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Copies all values of a given competitor. | ||||
|  * | ||||
|  * @param competitor The competitor to copy. | ||||
|  */ | ||||
| void Competitor::setCompetitor(const Competitor &competitor) { | ||||
|     setCode(competitor.m_code); | ||||
|     setName(competitor.m_name); | ||||
|     setNOC(competitor.m_noc); | ||||
|     setMark(competitor.m_mark); | ||||
|     setMedalType(competitor.m_medalType); | ||||
|     setStatistic(competitor.m_statistic); | ||||
|     setGold(competitor.m_gold); | ||||
|     setSilver(competitor.m_silver); | ||||
|     setBronze(competitor.m_bronze); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Replaces/sets the results of a competitor. | ||||
|  * | ||||
|  * @param results The results of the competitor. | ||||
|  * @return True, if successful. | ||||
|  */ | ||||
| bool Competitor::setResults(const QJsonObject &results) { | ||||
|     if (!results.contains("mark") | ||||
|         || !results.contains("medalType")) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     setMark(results["mark"].toString()); | ||||
|     setMedalType(results["medalType"].toString()); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Replaces/sets the won medals of a competitor. | ||||
|  * | ||||
|  * @param medals The won medals with their amount. | ||||
|  * @return True, if successful. | ||||
|  */ | ||||
| bool Competitor::setMedals(const map<QString, int> &medals) { | ||||
|     if (medals.find("ME_GOLD") == medals.end() | ||||
|         || medals.find("ME_SILVER") == medals.end() | ||||
|         || medals.find("ME_BRONZE") == medals.end()) return false; | ||||
| 
 | ||||
|     setGold(medals.find("ME_GOLD")->second); | ||||
|     setSilver(medals.find("ME_SILVER")->second); | ||||
|     setBronze(medals.find("ME_BRONZE")->second); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Static compare method, which can compare the result times or points of two competitors. | ||||
|  * Returns true, if the left competitor (lComp) got a real lesser score than the right competitor (rComp). | ||||
|  * | ||||
|  * @param lComp First competitor to compare. | ||||
|  * @param rComp Second competitor to compare. | ||||
|  * @return True, if the second competitor got a higher score. | ||||
|  */ | ||||
| bool Competitor::compareMark(Competitor lComp, Competitor rComp) { | ||||
|     QString l = lComp.mark(); | ||||
|     QString r = rComp.mark(); | ||||
| 
 | ||||
|     // check if values are numerical (-> not time values)
 | ||||
|     if (!l.contains(":") || !r.contains(":")) { | ||||
|         return l.toFloat() < r.toFloat(); | ||||
|     } | ||||
| 
 | ||||
|     // compare time values if not numerical
 | ||||
|     QString lTime(""), rTime(""); | ||||
| 
 | ||||
|     for (QChar c : l) if (c.isDigit()) lTime.append(c); | ||||
|     for (QChar c : r) if (c.isDigit()) rTime.append(c); | ||||
| 
 | ||||
|     return lTime.compare(rTime) < 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Static compare method, which can compare the amount of medals of two competitors. | ||||
|  * Gold has the highest priority, then m_silver and finally m_bronze. | ||||
|  * | ||||
|  * @param lComp First competitor to compare. | ||||
|  * @param rComp Second competitor to compare. | ||||
|  * @return True, if the second competitor got more or higher medals. | ||||
|  */ | ||||
| bool Competitor::compareMedals(Competitor lComp, Competitor rComp) { | ||||
|     // create difference between medal amounts
 | ||||
|     int gold = lComp.gold() - rComp.gold(); | ||||
|     int silver = lComp.silver() - rComp.silver(); | ||||
|     int bronze = lComp.bronze() - rComp.bronze(); | ||||
| 
 | ||||
|     // compare medal differences
 | ||||
|     return gold < 0 || (gold == 0 && (silver < 0 || (silver == 0 && bronze < 0))); | ||||
| } | ||||
|  |  | |||
|  | @ -1,39 +1,95 @@ | |||
| 
 | ||||
| #ifndef ITAT_CHALLANGE_OLYMPICS_COMPETITOR_H | ||||
| #define ITAT_CHALLANGE_OLYMPICS_COMPETITOR_H | ||||
| #pragma once | ||||
| 
 | ||||
| #include <QString> | ||||
| #include <QMap> | ||||
| #include <QJsonObject> | ||||
| #include <stdexcept> | ||||
| #include <QObject> | ||||
| #include <qqml.h> | ||||
| 
 | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| class Competitor { | ||||
| class Competitor : public QObject { | ||||
| 
 | ||||
|     Q_OBJECT | ||||
| 
 | ||||
|     Q_PROPERTY(int code READ code NOTIFY codeChanged) | ||||
|     Q_PROPERTY(QString name READ name NOTIFY nameChanged) | ||||
|     Q_PROPERTY(QString noc READ noc NOTIFY nocChanged) | ||||
| 
 | ||||
|     // results in a certain event/category
 | ||||
|     Q_PROPERTY(QString mark READ mark NOTIFY markChanged) | ||||
|     Q_PROPERTY(QString medalType READ medalType NOTIFY medalTypeChanged) | ||||
|     Q_PROPERTY(QString statistic READ statistic NOTIFY statisticChanged) | ||||
| 
 | ||||
|     // medal amounts in the whole discipline
 | ||||
|     Q_PROPERTY(int gold READ gold NOTIFY goldChanged) | ||||
|     Q_PROPERTY(int silver READ silver NOTIFY silverChanged) | ||||
|     Q_PROPERTY(int bronze READ bronze NOTIFY bronzeChanged) | ||||
| 
 | ||||
| public: | ||||
|     Competitor(const Competitor &competitor) { | ||||
|         this->code = competitor.code; | ||||
|         this->name = competitor.name; | ||||
|         this->noc = competitor.noc; | ||||
|     } | ||||
|     explicit Competitor(QObject *parent) : QObject(parent) {} | ||||
| 
 | ||||
|     Competitor(const QJsonObject &competitor) { | ||||
|         setCompetitor(competitor); | ||||
|     } | ||||
|     // getter
 | ||||
|     int code() const { return this->m_code; } | ||||
|     QString name() const { return this->m_name; } | ||||
|     QString noc() const { return this->m_noc; } | ||||
|     QString mark() const { return this->m_mark; } | ||||
|     QString medalType() const { return this->m_medalType; } | ||||
|     QString statistic() const { return this->m_statistic; } | ||||
|     int gold() const { return this->m_gold; } | ||||
|     int silver() const { return this->m_silver; } | ||||
|     int bronze() const { return this->m_bronze; } | ||||
| 
 | ||||
|     QString getCode() { return this->code; } | ||||
|     QString getName() { return this->name; } | ||||
|     QString getNOC() { return this->noc; } | ||||
|     // setter
 | ||||
|     void setCode(int code) { this->m_code = code; } | ||||
|     void setName(QString name) { this->m_name = name; } | ||||
|     void setNOC(QString noc) { this->m_noc = noc; } | ||||
|     void setMark(QString mark) { this->m_mark = mark; } | ||||
|     void setMedalType(QString medalType) { this->m_medalType = medalType; } | ||||
|     void setStatistic(QString stat) { this->m_statistic = stat; } | ||||
|     void setGold(int gold) { this->m_gold = gold; } | ||||
|     void setSilver(int silver) { this->m_silver = silver; } | ||||
|     void setBronze(int bronze) { this->m_bronze = bronze; } | ||||
| 
 | ||||
|     bool setResults(const QJsonObject &results); | ||||
|     bool setMedals(const map<QString, int> &medals); | ||||
| 
 | ||||
|     bool setCompetitor(const QJsonObject &competitor); | ||||
|     void setCompetitor(const Competitor &competitor); | ||||
| 
 | ||||
|     static bool compareName(Competitor lComp, Competitor rComp) { | ||||
|         return lComp.m_name.compare(rComp.m_name) < 0; | ||||
|     } | ||||
|     static bool compareNOC(Competitor lComp, Competitor rComp) { | ||||
|         return lComp.m_noc.compare(rComp.m_noc) < 0; | ||||
|     } | ||||
|     static bool compareMark(Competitor lComp, Competitor rComp); | ||||
|     static bool compareMedals(Competitor lComp, Competitor rComp); | ||||
| 
 | ||||
| 
 | ||||
| signals: | ||||
|     void codeChanged(); | ||||
|     void nameChanged(); | ||||
|     void nocChanged(); | ||||
|     void markChanged(); | ||||
|     void medalTypeChanged(); | ||||
|     void statisticChanged(); | ||||
|     void goldChanged(); | ||||
|     void silverChanged(); | ||||
|     void bronzeChanged(); | ||||
| 
 | ||||
| private: | ||||
|     QString code; | ||||
|     QString name; | ||||
|     QString noc; | ||||
|     int m_code; | ||||
|     QString m_name; | ||||
|     QString m_noc; | ||||
| 
 | ||||
|     QString m_mark; | ||||
|     QString m_medalType; | ||||
|     QString m_statistic; | ||||
| 
 | ||||
|     int m_gold; | ||||
|     int m_silver; | ||||
|     int m_bronze; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #endif //ITAT_CHALLANGE_OLYMPICS_COMPETITOR_H
 | ||||
|  |  | |||
|  | @ -1,15 +0,0 @@ | |||
| 
 | ||||
| #include "CompetitorWithResults.h" | ||||
| 
 | ||||
| bool CompetitorWithResults::setResults(const QJsonObject &results) { | ||||
|     if (!results.contains("mark") | ||||
|         || !results.contains("medalType")) { | ||||
|         throw invalid_argument("Results object of competitor is incomplete."); | ||||
|     } | ||||
| 
 | ||||
|     this->results = { | ||||
|             {QString("mark"), results["mark"].toString()}, | ||||
|             {QString("medalType"), results["medalType"].toString()} | ||||
|     }; | ||||
|     return true; | ||||
| } | ||||
|  | @ -1,28 +0,0 @@ | |||
| 
 | ||||
| #ifndef ITAT_CHALLANGE_OLYMPICS_COMPETITORWITHRESULTS_H | ||||
| #define ITAT_CHALLANGE_OLYMPICS_COMPETITORWITHRESULTS_H | ||||
| 
 | ||||
| #include "Competitor.h" | ||||
| #include <QString> | ||||
| #include <QMap> | ||||
| #include <QJsonObject> | ||||
| #include <stdexcept> | ||||
| 
 | ||||
| class CompetitorWithResults : public Competitor { | ||||
| 
 | ||||
| public: | ||||
|     CompetitorWithResults(const QJsonObject &competitor) : Competitor(competitor) { | ||||
|         if (!competitor.contains("results")) throw invalid_argument("Competitor does not contain results."); | ||||
|         QJsonObject results = competitor["results"].toObject(); | ||||
|         setResults(results); | ||||
|     } | ||||
| 
 | ||||
|     bool setResults(const QJsonObject &results); | ||||
| 
 | ||||
| private: | ||||
|     QMap<QString, QString> results; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #endif //ITAT_CHALLANGE_OLYMPICS_COMPETITORWITHRESULTS_H
 | ||||
|  | @ -1,22 +1,21 @@ | |||
| #include <QObject> | ||||
| #include "EventInfo.h" | ||||
| 
 | ||||
| EventInfo::EventInfo(QObject *parent) : QObject(parent) { | ||||
| } | ||||
| 
 | ||||
| QString EventInfo::eventName() const { | ||||
|   return m_eventName; | ||||
|   return this->m_eventName; | ||||
| } | ||||
| 
 | ||||
| void EventInfo::setEventName(const QString &newEventName) { | ||||
|   m_eventName = newEventName; | ||||
| } | ||||
| 
 | ||||
| QList<QString> EventInfo::competitors() const { | ||||
| QList<Competitor*> EventInfo::competitors() const { | ||||
|   return m_competitors; | ||||
| } | ||||
| 
 | ||||
| void EventInfo::setCompetitors(const QList<QString> &newCompetitors) { | ||||
|   m_competitors = newCompetitors; | ||||
| void EventInfo::setCompetitors(const QList<Competitor*> &newCompetitors) { | ||||
|   this->m_competitors = newCompetitors; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,28 +1,27 @@ | |||
| #ifndef ITAT_CHALLANGE_OLYMPICS_EVENT_H | ||||
| #define ITAT_CHALLANGE_OLYMPICS_EVENT_H | ||||
| #pragma once | ||||
| 
 | ||||
| #include "Competitor.h" | ||||
| #include <QObject> | ||||
| #include <QAbstractListModel> | ||||
| #include <qqml.h> | ||||
| 
 | ||||
| class EventInfo : QObject { | ||||
| class EventInfo : public QObject { | ||||
|   Q_OBJECT | ||||
|   // QML_ELEMENT
 | ||||
| 
 | ||||
|   Q_PROPERTY(QString eventName READ eventName WRITE setEventName); | ||||
|   Q_PROPERTY(QList<QString> competitors READ competitors WRITE setCompetitors); | ||||
|   Q_PROPERTY(QString eventName READ eventName CONSTANT) | ||||
|   Q_PROPERTY(QList<Competitor*> competitors READ competitors CONSTANT) | ||||
| 
 | ||||
|   public: | ||||
| public: | ||||
|   explicit EventInfo(QObject *parent = nullptr); | ||||
| 
 | ||||
|   QString eventName() const; | ||||
|   void setEventName(const QString &newEventName); | ||||
| 
 | ||||
|   QList<QString> competitors() const; | ||||
|   void setCompetitors(const QList<QString> &newCompetitors); | ||||
|   QList<Competitor*> competitors() const; | ||||
|   void setCompetitors(const QList<Competitor*> &newCompetitors); | ||||
| 
 | ||||
|   private: | ||||
| private: | ||||
|   QString m_eventName; | ||||
|   QList<QString> m_competitors; | ||||
|   QList<Competitor*> m_competitors; | ||||
| }; | ||||
| 
 | ||||
| #endif  | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| #include "FilterModel.h" | ||||
| #include "Sport.h" | ||||
| #include "SportModel.h" | ||||
| 
 | ||||
| FilterModel::FilterModel(QObject *parent) | ||||
|   : QSortFilterProxyModel(parent) { | ||||
|  |  | |||
|  | @ -1,18 +0,0 @@ | |||
| 
 | ||||
| #include "MedalWinner.h" | ||||
| 
 | ||||
| bool MedalWinner::setMedals(const QJsonObject &medals) { | ||||
|     if (!medals.contains("ME_GOLD") | ||||
|         || !medals.contains("ME_SILVER") | ||||
|         || !medals.contains("ME_BRONZE")) { | ||||
|         throw invalid_argument("Medal object of competitor is incomplete."); | ||||
|     } | ||||
| 
 | ||||
|     this->wonMedals = { | ||||
|             {QString("ME_GOLD"), medals["ME_GOLD"].toString()}, | ||||
|             {QString("ME_SILVER"), medals["ME_SILVER"].toString()}, | ||||
|             {QString("ME_BRONZE"), medals["ME_BRONZE"].toString()} | ||||
|     }; | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
|  | @ -1,35 +0,0 @@ | |||
| 
 | ||||
| #ifndef ITAT_CHALLANGE_OLYMPICS_MEDALWINNER_H | ||||
| #define ITAT_CHALLANGE_OLYMPICS_MEDALWINNER_H | ||||
| 
 | ||||
| #include "Competitor.h" | ||||
| #include <QMap> | ||||
| #include <QJsonObject> | ||||
| #include <stdexcept> | ||||
| 
 | ||||
| class MedalWinner : public Competitor { | ||||
| 
 | ||||
| public: | ||||
|     MedalWinner(const MedalWinner &medalWinner) : Competitor(medalWinner) { | ||||
|         this->wonMedals = { | ||||
|                 {QString("ME_GOLD"), medalWinner.wonMedals.value("ME_GOLD")}, | ||||
|                 {QString("ME_SILVER"), medalWinner.wonMedals.value("ME_SILVER")}, | ||||
|                 {QString("ME_BRONZE"), medalWinner.wonMedals.value("ME_BRONZE")} | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     MedalWinner(const QJsonObject &competitor) : Competitor(competitor) { | ||||
|         if (competitor.contains("medals")) throw invalid_argument("Competitor has no medals."); | ||||
|         QJsonObject medals = competitor["medals"].toObject(); | ||||
|         setMedals(medals); | ||||
|     } | ||||
| 
 | ||||
|     bool setMedals(const QJsonObject &medals); | ||||
| 
 | ||||
| private: | ||||
|     QMap<QString, QString> wonMedals; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #endif //ITAT_CHALLANGE_OLYMPICS_MEDALWINNER_H
 | ||||
|  | @ -1,127 +0,0 @@ | |||
| #ifndef ITAT_CHALLANGE_OLYMPICS_SPORT_H | ||||
| #define ITAT_CHALLANGE_OLYMPICS_SPORT_H | ||||
| 
 | ||||
| #include <QAbstractListModel> | ||||
| #include <QNetworkAccessManager> | ||||
| #include <qcontainerfwd.h> | ||||
| #include <set> | ||||
| 
 | ||||
| #include <QJsonObject> | ||||
| #include <QJsonDocument> | ||||
| #include <QString> | ||||
| #include "EventInfo.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| class SportModel : public QAbstractListModel { | ||||
|   Q_OBJECT | ||||
| 
 | ||||
|   Q_PROPERTY(QString discipline READ discipline WRITE setDiscipline NOTIFY disciplineChanged); | ||||
| 
 | ||||
|   public:  | ||||
|     enum Role { | ||||
|       EventName = Qt::UserRole + 1, | ||||
|       Competitors | ||||
|     }; | ||||
| 
 | ||||
|     explicit SportModel(QObject *parent = nullptr); | ||||
| 
 | ||||
|     virtual int rowCount(const QModelIndex &parent) const override; | ||||
|     virtual QVariant data(const QModelIndex &index, int role) const override; | ||||
|     virtual QHash<int, QByteArray> roleNames() const override; | ||||
| 
 | ||||
| 
 | ||||
|     QString discipline() const; | ||||
|     void setDiscipline(const QString &discipline); | ||||
|   public slots: | ||||
|     void request(QString discipline); | ||||
|     void parseData(); | ||||
| 
 | ||||
|   signals: | ||||
|     void disciplineChanged(); | ||||
| 
 | ||||
|   private: | ||||
|     QList<EventInfo*> m_sportList; | ||||
|     QString m_discipline; | ||||
|     QNetworkAccessManager m_networkManager; | ||||
|     QNetworkReply *m_reply = nullptr; | ||||
| }; | ||||
| 
 | ||||
| class Sport { | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     Sport(QJsonObject discipline) { | ||||
|         this->discipline = discipline; | ||||
|     } | ||||
| 
 | ||||
|     set<QString> getCategories(); | ||||
|     QJsonArray getCompetitorsByCategory(QString category); | ||||
|     QJsonArray getCompetitorsWithMedal(); | ||||
| 
 | ||||
|     // filter to change the current competitor array
 | ||||
|     void lastName(QJsonArray &competitors); | ||||
|     void filterByName(QJsonArray &competitors, QString name); | ||||
|     void filterByCountry(QJsonArray &competitors, QString nocShort); | ||||
| 
 | ||||
|     // sort functions to change the order of the current competitor array
 | ||||
|     void sortByName(QJsonArray &competitors); | ||||
|     void sortByCountry(QJsonArray &competitors); | ||||
|     void sortByResult(QJsonArray &competitors); | ||||
|     void reverseOrder(QJsonArray &competitors); | ||||
| 
 | ||||
|     // statistic function(s)
 | ||||
|     void addRelativeToFirst(QJsonArray &competitors); | ||||
| 
 | ||||
|     void setDiscipline(QJsonObject discipline) { | ||||
|         this->discipline = QJsonObject(discipline); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     /*
 | ||||
|      * Analysis of provided competitor objects: | ||||
|      * | ||||
|      * Attributes: | ||||
|      * - code | ||||
|      * - noc (national olympics comittee) | ||||
|      * - name (sometimes the country name? mostly the competitors name) | ||||
|      * - order | ||||
|      * [- results] (only if the results are out!) | ||||
|      * | ||||
|      * | ||||
|      * Analysis of provided result objects: | ||||
|      * | ||||
|      * Attributes: | ||||
|      * - position | ||||
|      * - mark | ||||
|      * - medalType | ||||
|      * - irk | ||||
|      * [- winnerLoserTie] (only if provided in the discipline?) | ||||
|      * | ||||
|      * Analysis of where to find the medal winners: | ||||
|      * | ||||
|      * Search for ... in category name. | ||||
|      * - "Bronze" | ||||
|      * - "Gold" | ||||
|      * - "Final" | ||||
|      * | ||||
|      * ! ATTENTION ! | ||||
|      * When searching for "Final" there might be "Final A", "Final B", etc. | ||||
|      * The results will only be in ONE of these categories! | ||||
|      * -> which is good... cause then we can count occurences. | ||||
|      */ | ||||
|     QJsonObject discipline; | ||||
| 
 | ||||
|     void filterCompetitors(QJsonArray &competitors, QString attribute, QString filter); | ||||
|     void sortCompetitors(QJsonArray &competitors,  function<bool (const QJsonValue &left, const QJsonValue &right)> compare); | ||||
| 
 | ||||
|     bool validateDiscipline(); | ||||
|     QJsonObject createCompetitorWithMedals(QJsonObject medalComp); | ||||
| 
 | ||||
|     float calcRelativeStat(QString ref, QString val); | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #endif  | ||||
|  | @ -1,4 +1,4 @@ | |||
| #include "Sport.h" | ||||
| #include "SportModel.h" | ||||
| #include "Competitor.h" | ||||
| 
 | ||||
| // categories
 | ||||
|  | @ -8,8 +8,7 @@ | |||
| #include <set> | ||||
| 
 | ||||
| // sorting and filtering
 | ||||
| #include <map> | ||||
| #include <algorithm> | ||||
| //#include <algorithm>
 | ||||
| #include <regex> | ||||
| 
 | ||||
| // float to string formatting
 | ||||
|  | @ -42,7 +41,7 @@ QVariant SportModel::data(const QModelIndex &index, int role) const { | |||
|         return event->eventName(); | ||||
| 
 | ||||
|       case Competitors: | ||||
|         return event->competitors(); | ||||
|         return QVariant::fromValue(event->competitors()); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | @ -82,14 +81,14 @@ void SportModel::parseData() { | |||
|       qDeleteAll(m_sportList); | ||||
|       m_sportList.clear(); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|       QByteArray strReply = m_reply->readAll(); | ||||
| 
 | ||||
|       //parse json
 | ||||
|       // qDebug() << "Response:" << strReply;
 | ||||
|       QJsonDocument jsonDocument = QJsonDocument::fromJson(strReply); | ||||
| 
 | ||||
|       map<QString, map<QString, int>> medals = getMedalsOfCompetitors(); | ||||
| 
 | ||||
|       QJsonArray sports = jsonDocument["units"].toArray(); | ||||
|       for (const auto &sport : sports) { | ||||
|         QJsonObject entry = sport.toObject(); | ||||
|  | @ -97,15 +96,20 @@ void SportModel::parseData() { | |||
|         EventInfo *event = new EventInfo(this); | ||||
|         event->setEventName(entry["eventUnitName"].toString()); | ||||
| 
 | ||||
|         QList<QString> competitors; | ||||
|         QList<Competitor*> competitors; | ||||
|         for (const auto &competitor : entry["competitors"].toArray()) { | ||||
|           competitors << competitor.toObject()["name"].toString(); | ||||
|           Competitor *comp = new Competitor(this); | ||||
|           comp->setCompetitor(competitor.toObject()); | ||||
|           if (medals.find(comp->name()) != medals.end()) comp->setMedals(medals.find(comp->name())->second); | ||||
|           competitors << comp; | ||||
|         } | ||||
|         addRelativeToFirst(competitors); | ||||
|         event->setCompetitors(competitors); | ||||
| 
 | ||||
|         qDebug() << entry["eventUnitName"].toString(); | ||||
|         m_sportList << event; | ||||
|       } | ||||
| 
 | ||||
|       endResetModel(); | ||||
|     } | ||||
| } | ||||
|  | @ -124,60 +128,17 @@ QJsonArray filter(QJsonArray input, function<bool (QJsonObject)> eval) { | |||
|     return output; | ||||
| } | ||||
| 
 | ||||
| // static compare function for specific attribute in competitors
 | ||||
| function<bool (const QJsonValue &left, const QJsonValue &right)> genCompare(QString attribute) { | ||||
|     return [attribute](const QJsonValue &left, const QJsonValue &right) { | ||||
|         QString l = left.toObject()[attribute].toString(); | ||||
|         QString r = right.toObject()[attribute].toString(); | ||||
|         return l.compare(r) < 0; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| // static compare function for the results of a competitor in a specific competition (also called mark)
 | ||||
| bool compareMark(const QJsonValue &left, const QJsonValue &right) { | ||||
|     // check if one competitor has no mark
 | ||||
|     QJsonObject l = left.toObject(); | ||||
|     if (!l.contains("results")) return true; | ||||
|     QJsonObject r = right.toObject(); | ||||
|     if (!r.contains("results")) return false; | ||||
| 
 | ||||
|     QString lMark = l["results"].toObject()["mark"].toString(); | ||||
|     QString rMark = r["results"].toObject()["mark"].toString(); | ||||
| 
 | ||||
|     // check if the marks are numerical values
 | ||||
|     if (!lMark.contains(":") && !rMark.contains(":")) return lMark.toFloat() < rMark.toFloat(); | ||||
| 
 | ||||
|     // compare time values if not numerical
 | ||||
|     QString lTime(""), rTime(""); | ||||
| 
 | ||||
|     for (QChar c : lMark) if (c.isDigit()) lTime.append(c); | ||||
|     for (QChar c : rMark) if (c.isDigit()) rTime.append(c); | ||||
| 
 | ||||
|     return lTime.compare(rTime) < 0; | ||||
| } | ||||
| 
 | ||||
| // static compare function for the amount of medals a competitor has gotten
 | ||||
| bool compareMedals(const QJsonValue &left, const QJsonValue &right) { | ||||
|     QJsonObject lMedals = left.toObject()["medals"].toObject(); | ||||
|     QJsonObject rMedals = right.toObject()["medals"].toObject(); | ||||
| 
 | ||||
|     int gold = lMedals["ME_GOLD"].toInt() - rMedals["ME_GOLD"].toInt(); | ||||
|     int silver = lMedals["ME_SILVER"].toInt() - rMedals["ME_SILVER"].toInt(); | ||||
|     int bronze = lMedals["ME_BRONZE"].toInt() - rMedals["ME_BRONZE"].toInt(); | ||||
| 
 | ||||
|     return gold < 0 || (gold == 0 && (silver < 0 || (silver == 0 && bronze < 0))); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Sport::lastName Reduce the full name to the part that is marked in capital letters (probably last name). | ||||
|  * @param competitors The competitors of one category. | ||||
|  */ | ||||
| void Sport::lastName(QJsonArray &competitors) { | ||||
| void SportModel::lastName(QList<Competitor*> &competitors) { | ||||
|     // validate competitors
 | ||||
|     if (competitors.isEmpty() || !competitors[0].toObject().contains("name")) return; | ||||
|     if (competitors.isEmpty()) return; | ||||
| 
 | ||||
|     for (int i = 0; i < competitors.size(); ++i) { | ||||
|         string fullName = competitors[i].toObject()["name"].toString().toUtf8().constData(); | ||||
|     for (int i = 0; i < competitors.size(); i++) { | ||||
|         Competitor* comp = competitors.value(i); | ||||
|         string fullName = comp->name().toUtf8().constData(); | ||||
| 
 | ||||
|         // regex to identify names, written in CAPS
 | ||||
|         regex r("[A-Z']{2,}"); | ||||
|  | @ -190,15 +151,10 @@ void Sport::lastName(QJsonArray &competitors) { | |||
|         for (string s : m) lastName = lastName + s + " "; | ||||
| 
 | ||||
|         // remove last space
 | ||||
|         QJsonValue nameValue = QJsonValue(QString(lastName.substr(0, lastName.size() - 1).c_str())); | ||||
|         QString name = QString(lastName.substr(0, lastName.size() - 1).c_str()); | ||||
| 
 | ||||
|         // create new object with replaced name
 | ||||
|         QJsonObject comp(competitors[i].toObject()); | ||||
|         comp.remove("name"); | ||||
|         comp.insert("name", nameValue); | ||||
| 
 | ||||
|         // replace competitor in array
 | ||||
|         competitors.replace(i, comp); | ||||
|         // replace competitor name in list
 | ||||
|         comp->setName(name); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -206,21 +162,21 @@ void Sport::lastName(QJsonArray &competitors) { | |||
|  * @brief Sport::validateDiscipline Validates the discipline object. Checks for the units attribute. | ||||
|  * @return True, if discipline contains units. | ||||
|  */ | ||||
| bool Sport::validateDiscipline() { | ||||
|     return this->discipline.contains("units"); | ||||
| bool SportModel::validateDiscipline() { | ||||
|     return this->o_discipline.contains("units"); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Sport::getCategories Reads all possible categories (also called units). | ||||
|  * @return A set of all category names. | ||||
|  */ | ||||
| set<QString> Sport::getCategories() { | ||||
| set<QString> SportModel::getCategories() { | ||||
|     set<QString> categoryNames; | ||||
| 
 | ||||
|     if (!validateDiscipline()) return categoryNames; | ||||
| 
 | ||||
|     // search in each unit for the category (named "eventUnitName")
 | ||||
|     for (const QJsonValueRef &unitRef : this->discipline["units"].toArray()) { | ||||
|     for (const QJsonValueRef &unitRef : this->o_discipline["units"].toArray()) { | ||||
|         QJsonObject unit = unitRef.toObject(); | ||||
| 
 | ||||
|         // validate unit
 | ||||
|  | @ -233,44 +189,16 @@ set<QString> Sport::getCategories() { | |||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Sport::getCompetitorsByCategory Searches for all competitors, who took part in the given category. | ||||
|  * @param category The category to search in. | ||||
|  * @return An QJsonArray with all competitors as QJsonValueRef, which can be casted to QJsonObject. | ||||
|  * @brief Sport::getMedalsOfCompetitor Filters all competitors, who have at least one medal. These objects are different from getCompetitorsByCategory !!! | ||||
|  * @return All competitors, who won at least one medal. Structure of one competitor: {code, name, m_noc, medals{ME_GOLD, ME_SILVER, ME_BRONZE}} | ||||
|  */ | ||||
| QJsonArray Sport::getCompetitorsByCategory(QString category) { | ||||
|     QJsonArray competitors; | ||||
| map<QString, map<QString, int>> SportModel::getMedalsOfCompetitors() { | ||||
|     map<QString, map<QString, int>> competitors; | ||||
| 
 | ||||
|     if (!validateDiscipline()) return competitors; | ||||
| 
 | ||||
|     for (const QJsonValueRef &unitRef : this->discipline["units"].toArray()) { | ||||
|         QJsonObject unit = unitRef.toObject(); | ||||
| 
 | ||||
|         // validate unit
 | ||||
|         if (!unit.contains("eventUnitName") || !unit.contains("competitors")) continue; | ||||
| 
 | ||||
|         // search all units with the same category
 | ||||
|         if (unit["eventUnitName"].toString().compare(category, Qt::CaseSensitive) != 0) continue; | ||||
| 
 | ||||
|         // add all competitors from one unit
 | ||||
|         for (const QJsonValueRef &compRef : unit["competitors"].toArray()) { | ||||
|             competitors.push_back(compRef.toObject()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return QJsonArray(competitors); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Sport::getCompetitorsWithMedal Filters all competitors, who have at least one medal. These objects are different from getCompetitorsByCategory !!! | ||||
|  * @return All competitors, who won at least one medal. Structure of one competitor: {code, name, noc, medals{ME_GOLD, ME_SILVER, ME_BRONZE}} | ||||
|  */ | ||||
| QJsonArray Sport::getCompetitorsWithMedal() { | ||||
|     map<QString, QJsonObject> competitors; | ||||
| 
 | ||||
|     if (!validateDiscipline()) return QJsonArray(); | ||||
| 
 | ||||
|     // filter all units, which have medal events
 | ||||
|     QJsonArray units = filter(this->discipline["units"].toArray(), [](QJsonObject unit){ | ||||
|     QJsonArray units = filter(this->o_discipline["units"].toArray(), [](QJsonObject unit){ | ||||
|         // search all units with Final, Gold or Bronze in their name, because these are the categories with the medal winners
 | ||||
|         QString unitName = unit["eventUnitName"].toString(); | ||||
|         return unitName.contains("Bronze", Qt::CaseSensitive) | ||||
|  | @ -305,56 +233,19 @@ QJsonArray Sport::getCompetitorsWithMedal() { | |||
| 
 | ||||
|             // check if competitor has other medal(s)
 | ||||
|             if (competitors.find(name) == competitors.end()) { | ||||
|                 competitors.insert({name, createCompetitorWithMedals(medalComp)}); | ||||
|                 map<QString, int> emptyMedalObject = { | ||||
|                     {"ME_GOLD", 0}, | ||||
|                     {"ME_SILVER", 0}, | ||||
|                     {"ME_BRONZE", 0} | ||||
|                 }; | ||||
|                 competitors.insert({name, emptyMedalObject}); | ||||
|             } | ||||
| 
 | ||||
|             // update the medal count
 | ||||
|             QJsonObject updatedMedalCount = QJsonObject(competitors.find(name)->second["medals"].toObject()); | ||||
| 
 | ||||
|             int amount = updatedMedalCount[medalType].toInt() + 1; | ||||
|             updatedMedalCount.remove(medalType); | ||||
|             updatedMedalCount.insert(medalType, amount); | ||||
| 
 | ||||
|             // create new medals QJsonObject and set it in the map
 | ||||
|             competitors.find(name)->second["medals"] = updatedMedalCount; | ||||
|             competitors.find(name)->second.find(medalType)->second++; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // convert map to QJsonArray
 | ||||
|     QJsonArray output; | ||||
|     for (const pair<QString, QJsonObject> &competitor : competitors) { | ||||
|         output.append(competitor.second); | ||||
|     } | ||||
| 
 | ||||
|     return output; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Sport::createCompetitorWithMedals Creates a competitor QJsonObject with the following attributes: code, name, noc, medals{ME_GOLD, ME_SILVER, ME_BRONZE} | ||||
|  * @param comp The original competitor object. | ||||
|  * @return A competitor object with medal counts. | ||||
|  */ | ||||
| QJsonObject Sport::createCompetitorWithMedals(QJsonObject comp) { | ||||
|     // repair competitor if something is missing
 | ||||
|     if (!comp.contains("code")) comp.insert("code", "0"); | ||||
|     if (!comp.contains("name")) comp.insert("code", ""); | ||||
|     if (!comp.contains("noc")) comp.insert("code", ""); | ||||
| 
 | ||||
|     // create new competitor QJsonObject and add it to the competitor map
 | ||||
|     QJsonObject medals { | ||||
|         {"ME_GOLD", 0}, | ||||
|         {"ME_SILVER", 0}, | ||||
|         {"ME_BRONZE", 0} | ||||
|     }; | ||||
| 
 | ||||
|     QJsonObject medalComp { | ||||
|         {"code", comp["code"].toString()}, | ||||
|         {"name", comp["name"].toString()}, | ||||
|         {"noc", comp["noc"].toString()}, | ||||
|         {"medals", medals} | ||||
|     }; | ||||
| 
 | ||||
|     return medalComp; | ||||
|     return competitors; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -362,8 +253,8 @@ QJsonObject Sport::createCompetitorWithMedals(QJsonObject comp) { | |||
|  * @param competitors The competitors of one category. | ||||
|  * @param name The (part of the) name to search for. | ||||
|  */ | ||||
| void Sport::filterByName(QJsonArray &competitors, QString name) { | ||||
|     filterCompetitors(competitors, QString("name"), name); | ||||
| void SportModel::filterByName(QList<Competitor*> &competitors, QString name) { | ||||
|     filterCompetitors(competitors, name); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -371,8 +262,8 @@ void Sport::filterByName(QJsonArray &competitors, QString name) { | |||
|  * @param competitors The competitors of one category. | ||||
|  * @param nocShort The (part of the) national olympics comittee short name to search for. | ||||
|  */ | ||||
| void Sport::filterByCountry(QJsonArray &competitors, QString nocShort) { | ||||
|     filterCompetitors(competitors, QString("noc"), nocShort); | ||||
| void SportModel::filterByCountry(QList<Competitor*> &competitors, QString nocShort) { | ||||
|     filterCompetitors(competitors, nocShort); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -381,16 +272,12 @@ void Sport::filterByCountry(QJsonArray &competitors, QString nocShort) { | |||
|  * @param attribute The attribute to filter by. | ||||
|  * @param filter The string, which should be contained. | ||||
|  */ | ||||
| void Sport::filterCompetitors(QJsonArray &competitors, QString attribute, QString filter) { | ||||
| void SportModel::filterCompetitors(QList<Competitor*> &competitors, QString filter) { | ||||
|     for (int i = 0; i < competitors.size(); i++) { | ||||
|         QJsonObject comp = competitors[i].toObject(); | ||||
| 
 | ||||
|         if (!comp.contains(attribute) || !comp[attribute].toString().contains(filter, Qt::CaseInsensitive)) { | ||||
|             // remove the competitor, if the attribute does not fit the filter string
 | ||||
|             competitors.removeAt(i); | ||||
|         if (!competitors.value(i)->noc().contains(filter)) { | ||||
|             competitors.remove(i); | ||||
|             i--; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -398,89 +285,77 @@ void Sport::filterCompetitors(QJsonArray &competitors, QString attribute, QStrin | |||
|  * @brief Sport::sortByName Sort the competitors by their name (alphabetical, ascending). | ||||
|  * @param competitors The competitors of one category. | ||||
|  */ | ||||
| void Sport::sortByName(QJsonArray &competitors) { | ||||
|     sortCompetitors(competitors, genCompare( QString("name") )); | ||||
| } | ||||
| //void SportModel::sortByName(QList<Competitor*> &competitors) {
 | ||||
| //    if (competitors.isEmpty()) return;
 | ||||
| //    std::sort(competitors.begin(), competitors.end(), Competitor::compareName);
 | ||||
| //}
 | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Sport::sortByCountry Sort the competitors by their national olympic comittee short name (alphabetical, ascending). | ||||
|  * @param competitors The competitors of one category. | ||||
|  */ | ||||
| void Sport::sortByCountry(QJsonArray &competitors) { | ||||
|     sortCompetitors(competitors, genCompare( QString("noc") )); | ||||
| } | ||||
| //void SportModel::sortByCountry(QList<Competitor*> &competitors) {
 | ||||
| //    if (competitors.isEmpty()) return;
 | ||||
| //    std::sort(competitors.begin(), competitors.end(), Competitor::compareNOC);
 | ||||
| //}
 | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Sport::sortByResult Sort the competitors by their results in one specific category (numerical, ascending). | ||||
|  * @param competitors The competitors of one category. | ||||
|  */ | ||||
| void Sport::sortByResult(QJsonArray &competitors) { | ||||
|     if (competitors.isEmpty()) return; | ||||
| 
 | ||||
|     QJsonObject comp = competitors[0].toObject(); | ||||
| 
 | ||||
|     if (comp.contains("results")) sortCompetitors(competitors, compareMark); | ||||
|     else if (comp.contains("medals")) sortCompetitors(competitors, compareMedals); | ||||
| } | ||||
| //void SportModel::sortByResult(QList<Competitor*> &competitors) {
 | ||||
| //    if (competitors.isEmpty()) return;
 | ||||
| //    std::sort(competitors.begin(), competitors.end(), Competitor::compareMark);
 | ||||
| //}
 | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Sport::sortCompetitors Sorts the given QJsonArray according to the compare function. | ||||
|  * @brief Sport::sortByMedals Sort the competitors by their medal amounts in one specific category (numerical, ascending). | ||||
|  * @param competitors The competitors of one category. | ||||
|  * @param compare A function to compare two competitors with each other. This defines the order. | ||||
|  */ | ||||
| void Sport::sortCompetitors(QJsonArray &competitors, function<bool (const QJsonValue &left, const QJsonValue &right)> compare) { | ||||
|     make_heap(competitors.begin(), competitors.end(), compare); | ||||
|     sort_heap(competitors.begin(), competitors.end(), compare); | ||||
| } | ||||
| //void SportModel::sortByMedals(QList<Competitor*> &competitors) {
 | ||||
| //    if (competitors.isEmpty()) return;
 | ||||
| //    std::sort(competitors.begin(), competitors.end(), Competitor::compareMedals);
 | ||||
| //}
 | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Sport::reverseOrder Reverses the order of the competitors. | ||||
|  * @param competitors The competitors of one category. | ||||
|  */ | ||||
| void Sport::reverseOrder(QJsonArray &competitors) { | ||||
| void SportModel::reverseOrder(QList<Competitor*> &competitors) { | ||||
|     int iterations = competitors.size() / 2; // automatically rounds down
 | ||||
| 
 | ||||
|     for (int i = 0; i < iterations; i++) { | ||||
|         QJsonObject temp = competitors[i].toObject(); | ||||
|         competitors[i] = competitors[competitors.size() - 1 - i].toObject(); | ||||
|         competitors[competitors.size() - 1 - i] = temp; | ||||
|         Competitor *left = competitors.value(i); | ||||
|         Competitor *right = competitors.value(competitors.size() - 1 - i); | ||||
| 
 | ||||
|         competitors.replace(i, right); | ||||
|         competitors.replace(competitors.size() - 1 - i, left); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Sport::addRelativeToFirst Adds a relative value to the result of all competitors. Relative to the first competitor in the QJsonArray. | ||||
|  * Stores the statistic in obj->results->stat for each competitor. | ||||
|  * Stores the m_statistic in obj->results->stat for each competitor. | ||||
|  * @param competitors The competitors of one category. | ||||
|  */ | ||||
| void Sport::addRelativeToFirst(QJsonArray &competitors) { | ||||
| void SportModel::addRelativeToFirst(QList<Competitor*> &competitors) { | ||||
|     if (competitors.isEmpty()) return; | ||||
| 
 | ||||
|     QJsonObject reference = competitors[0].toObject(); | ||||
| 
 | ||||
|     // validate competitors
 | ||||
|     if (!reference.contains("results")) return; | ||||
| 
 | ||||
|     QString refVal = reference["results"].toObject()["mark"].toString(); | ||||
|     QString reference = competitors.value(0)->mark(); | ||||
| 
 | ||||
|     for (int i = 0; i < competitors.size(); i++) { | ||||
|         QJsonObject competitor = competitors[i].toObject(); | ||||
|         QJsonObject results = competitor["results"].toObject(); | ||||
|         Competitor *comp = competitors.value(i); | ||||
| 
 | ||||
|         if (results.contains("stat")) results.remove("stat"); | ||||
|         QString result = comp->mark(); | ||||
| 
 | ||||
|         // format relative float value to string with 2 digits after decimal point and sign
 | ||||
|         stringstream sstream; | ||||
|         sstream << fixed << setprecision(2) << calcRelativeStat(refVal, results["mark"].toString()); | ||||
|         sstream << fixed << setprecision(2) << calcRelativeStat(reference, result); | ||||
|         QString stat(sstream.str().c_str()); | ||||
|         stat.append("%"); | ||||
|         if (stat.at(0).isNumber()) stat = QString("+").append(stat); | ||||
| 
 | ||||
|         results.insert("stat", stat); | ||||
| 
 | ||||
|         competitor.remove("results"); | ||||
|         competitor.insert("results", results); | ||||
| 
 | ||||
|         competitors.replace(i, competitor); | ||||
|         comp->setStatistic(stat); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -491,7 +366,7 @@ void Sport::addRelativeToFirst(QJsonArray &competitors) { | |||
|  * @param val The value to calculate the deviation from. | ||||
|  * @return The deviation from ref to val in percent. | ||||
|  */ | ||||
| float Sport::calcRelativeStat(QString ref, QString val) { | ||||
| float SportModel::calcRelativeStat(QString ref, QString val) { | ||||
|     // check if the value is not a time
 | ||||
|     if (!ref.contains(":") && !val.contains(":")) { | ||||
|         float fRef = ref.toFloat(); | ||||
							
								
								
									
										83
									
								
								src/model/SportModel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,83 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <QAbstractListModel> | ||||
| #include <QNetworkAccessManager> | ||||
| #include <qcontainerfwd.h> | ||||
| #include <set> | ||||
| 
 | ||||
| #include <QJsonObject> | ||||
| #include <QJsonDocument> | ||||
| #include <QString> | ||||
| #include <QList> | ||||
| #include "EventInfo.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| class SportModel : public QAbstractListModel | ||||
| { | ||||
|   Q_OBJECT | ||||
| 
 | ||||
|   Q_PROPERTY(QString discipline READ discipline WRITE setDiscipline NOTIFY disciplineChanged); | ||||
| 
 | ||||
| public: | ||||
|   enum Role | ||||
|   { | ||||
|     EventName = Qt::UserRole + 1, | ||||
|     Competitors | ||||
|   }; | ||||
| 
 | ||||
|   explicit SportModel(QObject *parent = nullptr); | ||||
| 
 | ||||
|   void setDiscipline(QJsonObject discipline) | ||||
|   { | ||||
|       this->o_discipline = QJsonObject(discipline); | ||||
|   } | ||||
| 
 | ||||
|   virtual int rowCount(const QModelIndex &parent) const override; | ||||
|   virtual QVariant data(const QModelIndex &index, int role) const override; | ||||
|   virtual QHash<int, QByteArray> roleNames() const override; | ||||
| 
 | ||||
|   set<QString> getCategories(); | ||||
|   map<QString, map<QString, int>> getMedalsOfCompetitors(); | ||||
| 
 | ||||
|   // filter to change the current competitor list
 | ||||
|   void lastName(QList<Competitor*> &competitors); | ||||
|   void filterByName(QList<Competitor*> &competitors, QString name); | ||||
|   void filterByCountry(QList<Competitor*> &competitors, QString nocShort); | ||||
| 
 | ||||
|   // sort functions to change the order of the current competitor list
 | ||||
| //  void sortByName(QList<Competitor*> &competitors);
 | ||||
| //  void sortByCountry(QList<Competitor*> &competitors);
 | ||||
| //  void sortByResult(QList<Competitor*> &competitors);
 | ||||
| //  void sortByMedals(QList<Competitor*> &competitors);
 | ||||
|   void reverseOrder(QList<Competitor*> &competitors); | ||||
| 
 | ||||
|   // statistic function
 | ||||
|   void addRelativeToFirst(QList<Competitor*> &competitors); | ||||
| 
 | ||||
|   QString discipline() const; | ||||
|   void setDiscipline(const QString &discipline); | ||||
| 
 | ||||
| public slots: | ||||
|   void request(QString discipline); | ||||
|   void parseData(); | ||||
| 
 | ||||
| signals: | ||||
|     void disciplineChanged(); | ||||
| 
 | ||||
| private: | ||||
|   QList<EventInfo *> m_sportList; | ||||
|   QString m_discipline; | ||||
|   QNetworkAccessManager m_networkManager; | ||||
|   QNetworkReply *m_reply = nullptr; | ||||
| 
 | ||||
|   // data from api
 | ||||
|   QJsonObject o_discipline; | ||||
|   bool validateDiscipline(); | ||||
| 
 | ||||
|   void filterCompetitors(QList<Competitor*> &competitors, QString filter); // TODO ref instead of obj
 | ||||
| 
 | ||||
|   // function for statistic calculation
 | ||||
|   float calcRelativeStat(QString ref, QString val); | ||||
| 
 | ||||
| }; | ||||