Commit 724317ac authored by Martin Juhás's avatar Martin Juhás
Browse files

feat: add clustering endpoints

### Additions

* added new `ClusteringParamsInput` and `ClusteringParamsType`
* added new `ClusteringType` type
* added new `ClusteredTeamType` type
* added new query `achievedMilestonesClustering(teamIds: [ID!]!, clusteringParams: ClusteringParamsInput!, recalculate: Boolean): ClusteringType!`
* added new query `milestoneSequenceClustering(teamIds: [ID!]!, clusteringParams: ClusteringParamsInput!, recalculate: Boolean): ClusteringType!`

Closes #540
parent 098c3bfa
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -1460,3 +1460,20 @@ class AssignmentResultType(graphene.ObjectType):

    def resolve_unaffected_user_ids(self, info):
        return [access.user_id for access in self.preexisting_assignments]


class ClusteredTeamType(graphene.ObjectType):
    team = graphene.Field(TeamType, required=True)
    cluster = graphene.Int(required=True)
    x = graphene.Float(required=True)
    y = graphene.Float(required=True)

    def resolve_team(self, info):
        return ensure_exists(Team.objects.filter(id=self.team_id))


class ClusteringType(graphene.ObjectType):
    teams = graphene.List(graphene.NonNull(ClusteredTeamType), required=True)

    def resolve_teams(self, info):
        return [team for cluster in self.teams.values() for team in cluster]
+7 −0
Original line number Diff line number Diff line
import glob
import os.path
import typing
from collections import defaultdict
@@ -531,6 +532,12 @@ class ExerciseManager:
                continue
            details_type.objects.filter(id__in=ids).delete()

        for clustering_file in glob.glob(
            os.path.join(
                settings.CLUSTERING_STORAGE, rf"exercise-{exercise.id}*"
            )
        ):
            os.remove(clustering_file)
        # this means that an override was created, which needs to be manually deleted
        if exercise.config.definition_id is None:
            exercise.config.delete()
+281 −8

File changed.

Preview size limit exceeded, changes collapsed.

+3 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@ uvicorn = {extras = ["standard"], version = "^0.33"}
psycopg2-binary = "^2.9.9"
channels-redis = "^4.2.0"
opensearch-py = "^3.0.0"
numpy = "1.24.4"
scikit-learn = "1.3.2"
umap-learn = "0.5.5"

[tool.poetry.group.dev.dependencies]
mypy = "^1.4"
+17 −0
Original line number Diff line number Diff line
@@ -177,3 +177,20 @@ class ActionLogsFilterType(ActionLogsFilterBase, graphene.ObjectType):

class ActionLogsFilterInput(ActionLogsFilterBase, graphene.InputObjectType):
    pass


class ClusteringParamsBase:
    iterations = graphene.Int(required=False, default_value=50)
    neighbors = graphene.Int(required=False, default_value=2)
    components = graphene.Int(required=False, default_value=2)
    min_dist = graphene.Float(required=False, default_value=0.0)
    eps = graphene.Float(required=False, default_value=0.8)
    min_samples = graphene.Int(required=False, default_value=2)


class ClusteringParamsType(ClusteringParamsBase, graphene.ObjectType):
    pass


class ClusteringParamsInput(ClusteringParamsBase, graphene.InputObjectType):
    pass
Loading