Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
FROM python:3.9
FROM python:3.9.20

RUN apt-get update && apt-get install -y gcc build-essential && rm -rf /var/lib/apt/lists/*

ENV PYTHONUNBUFFERED 1


RUN curl -sSL https://install.python-poetry.org | python3 - --version 1.8.3
# Poetry location so future commands (below) work

ENV PATH $PATH:/root/.local/bin
# Want poetry to use system python of docker container
RUN poetry config virtualenvs.create false
RUN poetry config virtualenvs.in-project false

COPY pyproject.toml ./
COPY pyproject.toml poetry.lock ./

# Install dependencies
RUN poetry install

RUN poetry lock
RUN poetry install

WORKDIR /app
3,662 changes: 3,662 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package-mode = false

[tool.poetry.dependencies]
python = "3.9.20"
django = "2.2.17"
django = "2.2.28"
django-oauth-toolkit = "1.0.0"
django-cors-middleware = "1.5.0"
social-auth-core = "2.0.0"
Expand All @@ -18,9 +18,9 @@ django-extensions = "2.2.6"
channels = "2.4"
channels-redis = "3.2.0"
django-extra-fields = "0.9"
pillow = "8.0.1"
pillow = "10.3.0"
celery = "4.2.1"
gunicorn = "20.0.4"
gunicorn = "22.0.0"
urllib3 = ">=1.21.1,<1.25"
uvicorn = {version = "0.13.3", extras = ["standard"]}
pyyaml = "5.3.1"
Expand Down Expand Up @@ -66,7 +66,9 @@ pytest = "6.2.1"
pytest-django = "4.1.0"
pytest-pythonpath = "0.7.3"
selenium = "3.141.0"
jinja2 = "3.1.4"
requests = "2.32.2"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
build-backend = "poetry.core.masonry.api"
3 changes: 2 additions & 1 deletion src/apps/api/serializers/leaderboards.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from drf_writable_nested import WritableNestedModelSerializer
from rest_framework import serializers

from api.serializers.submissions import SubmissionLeaderBoardSerializer
from api.serializers.submission_leaderboard import SubmissionLeaderBoardSerializer

from competitions.models import Submission, Phase
from leaderboards.models import Leaderboard, Column

Expand Down
31 changes: 31 additions & 0 deletions src/apps/api/serializers/submission_leaderboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# api/serializers/submission_leaderboard.py
from rest_framework import serializers
from competitions.models import Submission
from leaderboards.models import SubmissionScore
from api.serializers.profiles import SimpleOrganizationSerializer


class SubmissionScoreSerializer(serializers.ModelSerializer):
index = serializers.IntegerField(source='column.index', read_only=True)
column_key = serializers.CharField(source='column.key', read_only=True)

class Meta:
model = SubmissionScore
fields = ('id', 'index', 'score', 'column_key')


class SubmissionLeaderBoardSerializer(serializers.ModelSerializer):
scores = SubmissionScoreSerializer(many=True)
owner = serializers.CharField(source='owner.username')
display_name = serializers.CharField(source='owner.display_name')
slug_url = serializers.CharField(source='owner.slug_url')
organization = SimpleOrganizationSerializer(allow_null=True)
created_when = serializers.DateTimeField(format="%Y-%m-%d %H:%M")

class Meta:
model = Submission
fields = (
'id', 'parent', 'owner', 'leaderboard_id', 'fact_sheet_answers',
'task', 'scores', 'display_name', 'slug_url', 'organization',
'detailed_result', 'created_when'
)
48 changes: 2 additions & 46 deletions src/apps/api/serializers/submissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,17 @@

from api.mixins import DefaultUserCreateMixin
from api.serializers import leaderboards
from api.serializers.profiles import SimpleOrganizationSerializer
# from api.serializers.profiles import SimpleOrganizationSerializer
from api.serializers.tasks import TaskSerializer
from api.serializers.submission_leaderboard import SubmissionScoreSerializer
from competitions.models import Submission, SubmissionDetails, CompetitionParticipant, Phase
from datasets.models import Data
from leaderboards.models import SubmissionScore
from utils.data import make_url_sassy

from tasks.models import Task
from queues.models import Queue


class SubmissionScoreSerializer(serializers.ModelSerializer):
index = serializers.IntegerField(source='column.index', read_only=True)
column_key = serializers.CharField(source='column.key', read_only=True)

class Meta:
model = SubmissionScore
fields = (
'id',
'index',
'score',
'column_key',
)


class SubmissionSerializer(serializers.ModelSerializer):
scores = SubmissionScoreSerializer(many=True)
filename = serializers.SerializerMethodField(read_only=True)
Expand Down Expand Up @@ -92,36 +78,6 @@ def get_can_make_submissions_public(self, instance):
return instance.phase.competition.can_participants_make_submissions_public


class SubmissionLeaderBoardSerializer(serializers.ModelSerializer):
scores = SubmissionScoreSerializer(many=True)
owner = serializers.CharField(source='owner.username')
display_name = serializers.CharField(source='owner.display_name')
slug_url = serializers.CharField(source='owner.slug_url')
organization = SimpleOrganizationSerializer(allow_null=True)
created_when = serializers.DateTimeField(format="%Y-%m-%d %H:%M")

class Meta:
model = Submission
fields = (
'id',
'parent',
'owner',
'leaderboard_id',
'fact_sheet_answers',
'task',
'scores',
'display_name',
'slug_url',
'organization',
'detailed_result',
'created_when'
)
extra_kwargs = {
"scores": {"read_only": True},
"owner": {"read_only": True},
}


class SubmissionCreationSerializer(DefaultUserCreateMixin, serializers.ModelSerializer):
"""Used for creation _and_ status updates..."""
data = serializers.SlugRelatedField(queryset=Data.objects.all(), required=False, allow_null=True, slug_field='key')
Expand Down
8 changes: 6 additions & 2 deletions src/apps/api/views/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,12 @@ def competitions_usage(request):
'datefield'
)
for su in query.order_by("-datefield", "competition__id"):
username = su['competition__created_by__username'] or ("user #" + su['competition__created_by__id']) or "unknown user"
email = su['competition__created_by__email'] or "no email"
competitions_usage.setdefault(su['datefield'].isoformat(), {})[su['competition__id']] = {
'snapshot_id': su['id'],
'title': su['competition__title'],
'organizer': su['competition__created_by__username'] + " (" + su['competition__created_by__email'] + ")",
'organizer': username + " (" + email + ")",
'created_when': su['competition__created_when'],
'datasets': su['datasets_total'],
}
Expand Down Expand Up @@ -275,9 +277,11 @@ def users_usage(request):
'datefield'
)
for su in query.order_by("-datefield", "user__id"):
username = su['user__username'] or ("user #" + su['user__id']) or "unknown user"
email = su['user__email'] or "no email"
users_usage.setdefault(su['datefield'].isoformat(), {})[su['user__id']] = {
'snapshot_id': su['id'],
'name': su['user__username'] + " (" + su['user__email'] + ")",
'name': username + " (" + email + ")",
'date_joined': su['user__date_joined'],
'datasets': su['datasets_total'],
'submissions': su['submissions_total'],
Expand Down
18 changes: 14 additions & 4 deletions src/apps/forums/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ class DeletePostView(ForumBaseMixin, LoginRequiredMixin, DeleteView):
def delete(self, request, *args, **kwargs):
self.object = self.get_object()

if self.object.thread.forum.competition.created_by == request.user or \
self.object.posted_by == request.user:

if self.object.posted_by == request.user or \
request.user in self.object.thread.forum.competition.collaborators.all() or \
self.object.thread.forum.competition.created_by == request.user:
# If there are more posts in the thread, leave it around, otherwise delete it
if self.object.thread.posts.count() == 1:
success_url = self.object.thread.forum.get_absolute_url()
Expand Down Expand Up @@ -146,9 +146,19 @@ class ThreadDetailView(ForumBaseMixin, DetailView):
def get_context_data(self, **kwargs):
thread = self.object
context = super().get_context_data(**kwargs)
context['ordered_posts'] = thread.posts.all().order_by('date_created')\
ordered_posts = thread.posts.all().order_by('date_created')\
.select_related('thread__forum__competition__created_by', 'posted_by')\
.prefetch_related('thread__forum__competition__collaborators')

# Check if request.user has admin permissions
for post in ordered_posts:
post.user_is_admin = (
self.request.user == post.posted_by or
self.request.user == post.thread.forum.competition.created_by or
self.request.user in post.thread.forum.competition.collaborators.all()
)

context['ordered_posts'] = ordered_posts
return context


Expand Down
8 changes: 4 additions & 4 deletions src/static/riot/management.tag
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<div class="ui top attached tabular menu">
<div class="active item" data-tab="submissions">Submissions</div>
<div class="item" data-tab="datasets">Datasets and programs</div>
<div class="item" data-tab="bundles">Bundles</div>
<div class="item" data-tab="tasks">Tasks</div>
<div class="item" data-tab="bundles">Competition Bundles</div>
<div class="right menu">
<div class="item">
<help_button href="https://github.com/codalab/competitions-v2/wiki/Task-&-Dataset-Management"
Expand All @@ -20,12 +20,12 @@
<div class="ui bottom attached tab segment" data-tab="datasets">
<data-management></data-management>
</div>
<div class="ui bottom attached tab segment" data-tab="bundles">
<bundle-management></bundle-management>
</div>
<div class="ui bottom attached tab segment" data-tab="tasks">
<task-management></task-management>
</div>
<div class="ui bottom attached tab segment" data-tab="bundles">
<bundle-management></bundle-management>
</div>

<script>
let self = this
Expand Down
4 changes: 2 additions & 2 deletions src/templates/forums/thread_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<div class="posted-date">{{ post.date_created }}</div>
</div>
<div class="post-actions">
{% if thread.forum.competition.creator == request.user or post.posted_by == request.user %}
{% if post.user_is_admin %}
<i class="icon trash red remove-button" data-submission-pk="{{ post.pk }}"></i>
{% endif %}
</div>
Expand Down Expand Up @@ -62,7 +62,7 @@
$(document).ready(function () {
$('.remove-button').click(function (event) {
var submission_pk = event.target.dataset.submissionPk
if (confirm("Are you sure you want to delete this?")) {
if (confirm("Are you sure you want to delete this post?")) {
redirect_post("/forums/{{ thread.forum.pk }}/{{ thread.pk }}/delete/" + submission_pk + "/")
}
})
Expand Down
24 changes: 18 additions & 6 deletions src/utils/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,26 @@ def __init__(self, *args, **kwargs):
StorageClass = get_storage_class(settings.DEFAULT_FILE_STORAGE)

if settings.STORAGE_IS_S3:
BundleStorage = StorageClass(bucket=settings.AWS_STORAGE_PRIVATE_BUCKET_NAME)
PublicStorage = StorageClass(bucket=settings.AWS_STORAGE_BUCKET_NAME)
# Create separate instances for Bundle and Public storage
BundleStorage = StorageClass()
BundleStorage.bucket_name = settings.AWS_STORAGE_PRIVATE_BUCKET_NAME # Set the private bucket
PublicStorage = StorageClass()
PublicStorage.bucket_name = settings.AWS_STORAGE_BUCKET_NAME # Set the public bucket

elif settings.STORAGE_IS_GCS:
BundleStorage = StorageClass(bucket_name=settings.GS_PRIVATE_BUCKET_NAME)
PublicStorage = StorageClass(bucket_name=settings.GS_PUBLIC_BUCKET_NAME)
# For GCS
BundleStorage = StorageClass()
BundleStorage.bucket_name = settings.GS_PRIVATE_BUCKET_NAME # Set the private bucket
PublicStorage = StorageClass()
PublicStorage.bucket_name = settings.GS_PUBLIC_BUCKET_NAME # Set the public bucket

elif settings.STORAGE_IS_AZURE:
BundleStorage = StorageClass(azure_container=settings.BUNDLE_AZURE_CONTAINER)
PublicStorage = StorageClass(azure_container=settings.AZURE_CONTAINER)
# For Azure
BundleStorage = StorageClass()
BundleStorage.azure_container = settings.BUNDLE_AZURE_CONTAINER # Set the private container
PublicStorage = StorageClass()
PublicStorage.azure_container = settings.AZURE_CONTAINER # Set the public container

else:
raise NotImplementedError()

Expand Down