Skip to content

Commit

Permalink
Display global rating on ContestGlobalRatingView (#1187)
Browse files Browse the repository at this point in the history
* Change returned type in `OrganizationController` from `Organization` to `OrganizationDto`
* Add rating in `OrganizationDto`
* Display rating on frontend

Part of #1115
  • Loading branch information
petertrr authored Sep 8, 2022
1 parent a1d595f commit 5bbaf90
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 34 deletions.
10 changes: 7 additions & 3 deletions save-backend/backend-api-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1758,13 +1758,13 @@
},
"responses": {
"200": {
"description": "Successfully fetched non-deleted projects.",
"description": "Successfully fetched non-deleted organizations.",
"content": {
"*/*": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Organization"
"$ref": "#/components/schemas/OrganizationDto"
}
}
}
Expand All @@ -1777,7 +1777,7 @@
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Organization"
"$ref": "#/components/schemas/OrganizationDto"
}
}
}
Expand Down Expand Up @@ -7166,6 +7166,10 @@
"VIEWER"
]
}
},
"globalRating": {
"type": "number",
"format": "double"
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,21 @@ internal class OrganizationController(
summary = "Get non-deleted organizations.",
description = "Get non-deleted organizations.",
)
@ApiResponse(responseCode = "200", description = "Successfully fetched non-deleted projects.")
fun getNotDeletedOrganizations(@RequestBody(required = false) organizationFilters: OrganizationFilters?) =
organizationService.getNotDeletedOrganizations(organizationFilters).toFlux()
@ApiResponse(responseCode = "200", description = "Successfully fetched non-deleted organizations.")
fun getNotDeletedOrganizations(
@RequestBody(required = false) organizationFilters: OrganizationFilters?,
authentication: Authentication,
): Flux<OrganizationDto> =
organizationService.getNotDeletedOrganizations(organizationFilters)
.toFlux<Organization>()
.flatMap { organization ->
organizationService.getGlobalRating(organization.name, authentication).map {
organization to it
}
}
.map { (organization, rating) ->
organization.toDto().copy(globalRating = rating)
}

@GetMapping("/{organizationName}")
@PreAuthorize("permitAll()")
Expand Down Expand Up @@ -458,10 +470,7 @@ internal class OrganizationController(
"Could not find an organization with name $organizationName."
}
.flatMap {
projectService.getNotDeletedProjectsByOrganizationName(organizationName, authentication).collectList()
}
.map { projectsList ->
projectsList.sumOf { it.contestRating }
organizationService.getGlobalRating(organizationName, authentication)
}

private fun cleanupStorageData(testSuite: TestSuite) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.saveourtool.save.domain.OrganizationSaveStatus
import com.saveourtool.save.entities.Organization
import com.saveourtool.save.entities.OrganizationStatus
import com.saveourtool.save.filters.OrganizationFilters
import org.springframework.security.core.Authentication
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

Expand All @@ -15,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional
*/
@Service
class OrganizationService(
private val projectService: ProjectService,
private val organizationRepository: OrganizationRepository,
) {
/**
Expand Down Expand Up @@ -115,4 +117,16 @@ class OrganizationService(
* @return all organizations that were registered in SAVE
*/
fun findAll(): List<Organization> = organizationRepository.findAll()

/**
* @param organizationName
* @param authentication
* @return global rating of organization by name [organizationName] based on ratings of all projects under this organization
*/
fun getGlobalRating(organizationName: String, authentication: Authentication) =
projectService.getNotDeletedProjectsByOrganizationName(organizationName, authentication)
.collectList()
.map { projectsList ->
projectsList.sumOf { it.contestRating }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable
* @property description
* @property canCreateContests
* @property userRoles map where keys are usernames and values are their [Role]s
* @property globalRating
*/
@Serializable
data class OrganizationDto(
Expand All @@ -25,6 +26,7 @@ data class OrganizationDto(
val description: String = "",
val canCreateContests: Boolean = false,
val userRoles: Map<String, Role> = emptyMap(),
val globalRating: Double? = null,
) : Validatable {
/**
* Validation of organization name
Expand All @@ -47,12 +49,13 @@ data class OrganizationDto(
* Value that represents an empty [OrganizationDto]
*/
val empty = OrganizationDto(
"",
null,
null,
"",
false,
emptyMap(),
name = "",
dateCreated = null,
avatar = null,
description = "",
canCreateContests = false,
userRoles = emptyMap(),
globalRating = null,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package com.saveourtool.save.frontend.components.basic

import com.saveourtool.save.frontend.utils.toFixed
import csstype.*
import react.FC
import react.Props
Expand Down Expand Up @@ -80,7 +81,7 @@ private fun scoreCard() = FC<ScoreCardProps> { props ->
alignItems = AlignItems.center
alignSelf = AlignSelf.start
}
+"${props.contestScore}"
+"${props.contestScore.toFixed(2)}"
}
}
div {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
package com.saveourtool.save.frontend.components.views.contests

import com.saveourtool.save.domain.Role
import com.saveourtool.save.entities.Organization
import com.saveourtool.save.entities.OrganizationDto
import com.saveourtool.save.entities.Project
import com.saveourtool.save.filters.OrganizationFilters
import com.saveourtool.save.filters.ProjectFilters
Expand Down Expand Up @@ -63,7 +63,7 @@ external interface ContestGlobalRatingViewState : State, HasSelectedMenu<UserRat
/**
* All organizations
*/
var organizations: Array<Organization>
var organizations: Array<OrganizationDto>

/**
* All projects
Expand Down Expand Up @@ -91,7 +91,7 @@ class ContestGlobalRatingView : AbstractView<ContestGlobalRatingProps, ContestGl
"STRING_TEMPLATE_QUOTES",
)
private val tableWithOrganizationRating = tableComponent(
columns = columns<Organization> {
columns = columns<OrganizationDto> {
column(id = "index", header = "Position") {
Fragment.create {
td {
Expand Down Expand Up @@ -121,10 +121,10 @@ class ContestGlobalRatingView : AbstractView<ContestGlobalRatingProps, ContestGl
}
}
}
column(id = "rating", header = "Rating") {
column(id = "rating", header = "Rating") { cellProps ->
Fragment.create {
td {
+"4560"
+"${cellProps.value.globalRating?.toFixed(2)}"
}
}
}
Expand Down Expand Up @@ -185,10 +185,10 @@ class ContestGlobalRatingView : AbstractView<ContestGlobalRatingProps, ContestGl
}
}
}
column(id = "rating", header = "Rating") {
column(id = "rating", header = "Rating") { cellProps ->
Fragment.create {
td {
+"1370"
+"${cellProps.value.contestRating.toFixed(2)}"
}
}
}
Expand Down Expand Up @@ -236,7 +236,7 @@ class ContestGlobalRatingView : AbstractView<ContestGlobalRatingProps, ContestGl

private fun getOrganization(filterValue: OrganizationFilters) {
scope.launch {
val organizationsFromBackend: List<Organization> = post(
val organizationsFromBackend: List<OrganizationDto> = post(
url = "$apiUrl/organizations/not-deleted",
headers = jsonHeaders,
body = Json.encodeToString(filterValue),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

package com.saveourtool.save.frontend.components.views.contests

import com.saveourtool.save.entities.Organization
import com.saveourtool.save.entities.OrganizationDto
import com.saveourtool.save.entities.Project
import com.saveourtool.save.frontend.TabMenuBar
import com.saveourtool.save.frontend.externals.fontawesome.faArrowRight
Expand Down Expand Up @@ -39,7 +39,7 @@ enum class UserRatingTab {

companion object : TabMenuBar<UserRatingTab> {
// The string is the postfix of a [regexForUrlClassification] for parsing the url
private val postfixInRegex = values().map { it.name.lowercase() }.joinToString("|")
private val postfixInRegex = values().joinToString("|") { it.name.lowercase() }
override val nameOfTheHeadUrlSection = ""
override val defaultTab: UserRatingTab = UserRatingTab.ORGS
override val regexForUrlClassification = Regex("/${FrontendRoutes.CONTESTS_GLOBAL_RATING.path}/($postfixInRegex)")
Expand Down Expand Up @@ -76,18 +76,17 @@ private fun ChildrenBuilder.renderingProjectChampionsTable(projects: Set<Project
}
}

// FixMe: add rating after kirill's changes
div {
className = ClassName("col-lg-4")
p {
+"4560"
+"${project.contestRating.toFixed(2)}"
}
}
}
}
}

private fun ChildrenBuilder.renderingOrganizationChampionsTable(organizations: Set<Organization>) {
private fun ChildrenBuilder.renderingOrganizationChampionsTable(organizations: Set<OrganizationDto>) {
organizations.forEachIndexed { i, organization ->
div {
className = ClassName("row text-muted pb-3 mb-3 border-bottom border-gray mx-2")
Expand Down Expand Up @@ -115,11 +114,10 @@ private fun ChildrenBuilder.renderingOrganizationChampionsTable(organizations: S
}
}

// FixMe: add rating after kirill's changes
div {
className = ClassName("col-lg-4")
p {
+"4560"
+"${organization.globalRating?.toFixed(2)}"
}
}
}
Expand All @@ -133,9 +131,9 @@ private fun ChildrenBuilder.renderingOrganizationChampionsTable(organizations: S
private fun userRating() = VFC {
val (selectedTab, setSelectedTab) = useState(UserRatingTab.ORGS)

val (organizations, setOrganizations) = useState<Set<Organization>>(emptySet())
val (organizations, setOrganizations) = useState<Set<OrganizationDto>>(emptySet())
useRequest {
val organizationsFromBackend: List<Organization> = post(
val organizationsFromBackend: List<OrganizationDto> = post(
url = "$apiUrl/organizations/not-deleted",
headers = jsonHeaders,
body = undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,8 @@ internal fun ChildrenBuilder.multilineTextWithIndices(text: String) {
* @return true if string is invalid
*/
internal fun String?.isInvalid(maxLength: Int) = this.isNullOrBlank() || this.contains(" ") || this.length > maxLength

/**
* @param digits number of digits to round to
*/
internal fun Double.toFixed(digits: Int) = asDynamic().toFixed(digits)

0 comments on commit 5bbaf90

Please sign in to comment.