Skip to content
This repository has been archived by the owner on May 6, 2022. It is now read-only.

Commit

Permalink
Update challenge filters and sorting (#201)
Browse files Browse the repository at this point in the history
* Run openapi-generator before starting implemeting controller

* Add most challenge filters

* Update input data type property of challenge

* Enable to sort results accordingly to the schema

* Set debug=False

* Fix linter checks

* Add new challenge filters for orgs, organizers and sponsors

* Filter challenges by their ownerId

* Filter challenges by organizer user ids

* Fix list challenges when organizer_ids is None
  • Loading branch information
tschaffter authored Dec 9, 2021
1 parent d9127f2 commit 1047b54
Show file tree
Hide file tree
Showing 13 changed files with 992 additions and 50 deletions.
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
},
"private": true,
"scripts": {
"remove:models": "rm -fr server/openapi_server/models",
"generate:server": "npm run remove:models && openapi-generator-cli generate -g python-flask -o server -i $npm_config_specification",
"generate:server:latest": "npm run remove:models && openapi-generator-cli generate -g python-flask -o server -i https://sage-bionetworks.github.io/rocc-schemas/latest/openapi.json",
"generate:server:edge": "npm run remove:models && openapi-generator-cli generate -g python-flask -o server -i https://sage-bionetworks.github.io/rocc-schemas/edge/openapi.json",
"generate:server:version": "npm run remove:models && openapi-generator-cli generate -g python-flask -o server -i https://sage-bionetworks.github.io/rocc-schemas/${npm_config_schemas_version}/openapi.json",
"clean:models": "rm -fr server/openapi_server/models",
"generate": "npm run clean:models && openapi-generator-cli generate -g python-flask -o server -i $npm_config_specification",
"generate:latest": "npm run clean:models && openapi-generator-cli generate -g python-flask -o server -i https://sage-bionetworks.github.io/rocc-schemas/latest/openapi.json",
"generate:edge": "npm run clean:models && openapi-generator-cli generate -g python-flask -o server -i https://sage-bionetworks.github.io/rocc-schemas/edge/openapi.json",
"generate:version": "npm run clean:models && openapi-generator-cli generate -g python-flask -o server -i https://sage-bionetworks.github.io/rocc-schemas/${npm_config_schemas_version}/openapi.json",
"lint": "cd server && flake8",
"test": "cd server && tox",
"install:dependencies": "npm ci && cd server && pip install -r requirements.txt",
Expand Down
2 changes: 1 addition & 1 deletion server/openapi_server/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@


def main():
app.run(port=8080, debug=False)
app.run(port=8080, debug=True)


if __name__ == '__main__':
Expand Down
89 changes: 79 additions & 10 deletions server/openapi_server/controllers/challenge_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ def list_account_challenges(account_name, limit=None, offset=None, search_terms=
return res, status


def list_challenges(limit=None, offset=None, sort=None, direction=None, search_terms=None, topics=None, status=None, platform_ids=None, start_date_range=None): # noqa: E501
def list_challenges(limit=None, offset=None, sort=None, direction=None, search_terms=None, topics=None, input_data_types=None, status=None, platform_ids=None, difficulty=None, submission_types=None, incentive_types=None, start_date_range=None, org_ids=None, organizer_ids=None, sponsor_ids=None): # noqa: E501
"""List all the challenges
Returns all the challenges # noqa: E501
Expand All @@ -416,50 +416,119 @@ def list_challenges(limit=None, offset=None, sort=None, direction=None, search_t
:type limit: int
:param offset: Index of the first result that must be returned
:type offset: int
:param sort: Property used to sort the results that must be returned
:param sort: Properties used to sort the results that must be returned: * featured - featured challenge, from featured to non-featured. * startDate - start date of a challenge, from latest to oldest. * participantCount - number of participants of a challenge, from most to least. * viewCount - number of views of a challenge, from most to least. * starredCount - number of stargazers of a challenge, from most to least. * name - name of a challenge, from A to Z. * createdAt - when a challenge is created, from latest to oldest. * updatedAt - when a challenge is updated, from latest to oldest.
:type sort: str
:param direction: Can be either `asc` or `desc`. Ignored without `sort` parameter.
:type direction: str
:param search_terms: A string of search terms used to filter the results
:type search_terms: str
:param topics: Array of topics used to filter the results
:type topics: List[str]
:param input_data_types: Array of input data types used to filter the results
:type input_data_types: List[str]
:param status: Array of challenge status used to filter the results
:type status: list | bytes
:param platform_ids: Array of challenge platform ids used to filter the results
:type platform_ids: List[str]
:param difficulty: Array of challenge difficulty levels used to filter the results
:type difficulty: list | bytes
:param submission_types: Array of challenge submission types used to filter the results
:type submission_types: list | bytes
:param incentive_types: Array of challenge incentive types used to filter the results
:type incentive_types: list | bytes
:param start_date_range: Return challenges that start during the date range specified
:type start_date_range: dict | bytes
:param org_ids: Array of organization ids used to filter the results
:type org_ids: List[str]
:param organizer_ids: Array of organizer identifiers used to filter the results
:type organizer_ids: List[str]
:param sponsor_ids: Array of sponsor org identifiers used to filter the results
:type sponsor_ids: List[str]
:rtype: PageOfChallenges
"""
try:
# create topics filter
topics_q = Q(topics__in=topics) \
if topics is not None and len(topics) > 0 else Q()

# create status filter
status_q = Q(status__in=status) \
if status is not None else Q()

# create platform filter
platform_id_q = Q(platformId__in=platform_ids) \
if platform_ids is not None and len(platform_ids) > 0 else Q()

# create difficulty filter
# TODO query parameters platform_ids is plural. Query parameter is
# singular. Stay consistent.
difficulty_q = Q(difficulty__in=difficulty) \
if difficulty is not None else Q()

# create input data type filter
input_data_types_q = Q(inputDataTypes__in=input_data_types) \
if input_data_types is not None and len(input_data_types) > 0 else Q() # noqa: E501

# create submission type filter
submission_types_q = Q(submissionTypes__in=submission_types) \
if submission_types is not None and len(submission_types) > 0 else Q() # noqa: E501

# create incentive type filter
incentive_types_q = Q(incentiveTypes__in=incentive_types) \
if incentive_types is not None and len(incentive_types) > 0 else Q() # noqa: E501

# create start date filter
start_date_start = None
start_date_end = None
if start_date_range is not None and 'start' in start_date_range:
start_date_start = datetime.datetime.strptime(start_date_range['start'], '%Y-%m-%d') # noqa: E501
if start_date_range is not None and 'end' in start_date_range:
start_date_end = datetime.datetime.strptime(start_date_range['end'], '%Y-%m-%d') # noqa: E501

status_q = Q(status__in=status) \
if status is not None else Q()
# tag_ids_q = Q(tagIds__in=tag_ids) \
# if tag_ids is not None and len(tag_ids) > 0 else Q()
platform_id_q = Q(platformId__in=platform_ids) \
if platform_ids is not None and len(platform_ids) > 0 else Q()
startDate_start_q = Q(startDate__gte=start_date_start) \
if start_date_start is not None else Q()
startDate_end_q = Q(startDate__lte=start_date_end) \
if start_date_end is not None else Q()

db_challenges = DbChallenge.objects(status_q & platform_id_q & startDate_start_q & startDate_end_q) # noqa: E501
# create owner/org filter
owner_id_q = Q(ownerId__in=org_ids) \
if org_ids is not None and len(org_ids) > 0 else Q()

# create organizer filter
# organizer_ids is a list of User ids
organizers_q = Q()
if (organizer_ids is not None and len(organizer_ids) > 0):
db_organizer_users = DbUser.objects(id__in=organizer_ids)
organizer_logins = [User.from_dict(d.to_dict()).login for d in db_organizer_users] # noqa: E501
db_organizers = DbChallengeOrganizer.objects(login__in=organizer_logins) # noqa: E501
organizer_challenge_ids = [d.to_dict()['challengeId'] for d in db_organizers] # noqa: E501
organizers_q = Q(id__in=organizer_challenge_ids)

# apply filters except search terms
db_challenges = DbChallenge.objects(
topics_q &
status_q &
platform_id_q &
difficulty_q &
input_data_types_q &
submission_types_q &
incentive_types_q &
startDate_start_q &
startDate_end_q &
owner_id_q &
organizers_q
)

# apply filter by search terms
if search_terms is not None:
db_challenges = db_challenges.search_text(search_terms)

# sort results
if sort is not None:
order_by = ('-' if direction == 'desc' else '') + sort
db_challenges = db_challenges.order_by(order_by)

# paginate results
db_challenges = db_challenges.skip(offset).limit(limit)

challenges = [Challenge.from_dict(d.to_dict()) for d in db_challenges]
Expand Down
34 changes: 16 additions & 18 deletions server/openapi_server/dbmodels/challenge.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from bson import ObjectId
import datetime
from mongoengine import DateTimeField, Document, ReferenceField, StringField, ObjectIdField, URLField, ListField, IntField # noqa: E501
# , ListField
from mongoengine import DateTimeField, Document, ReferenceField, StringField, ObjectIdField, URLField, ListField, IntField, BooleanField # noqa: E501

from openapi_server.dbmodels.account import Account
from openapi_server.dbmodels.challenge_platform import ChallengePlatform
Expand All @@ -25,27 +24,26 @@ class Challenge(Document):
platformId = ReferenceField(ChallengePlatform)
readmeId = ReferenceField(ChallengeReadme)
topics = ListField(StringField(unique=True), default=[])
difficulty = StringField(
choices=["GoodForBeginners", "Intermediate", "Advanced"] # TODO: DRY
)
inputDataTypes = ListField(StringField(unique=True), default=[])
submissionTypes = ListField(StringField(
choices=["DockerImage", "PredictionFile", "Other"] # TODO: DRY
))
incentiveTypes = ListField(StringField(
# TODO: DRY
choices=["Monetary", "Publication", "SpeakingEngagement", "Other"] # noqa: E501
))
featured = BooleanField(default=False)
participantCount = IntField(default=0)
viewCount = IntField(default=0)
starredCount = IntField(default=0)
doi = StringField()
createdAt = DateTimeField(required=True, default=datetime.datetime.now)
updatedAt = DateTimeField(required=True, default=datetime.datetime.now)
v = IntField(db_field='__v')

# summary = StringField()
# startDate = DateTimeField()
# endDate = DateTimeField()
# url = URLField(required=True)
# status = StringField(
# required=True,
# choices=["active", "upcoming", "completed"] # TODO: DRY
# )
# tagIds = ListField(ReferenceField(Tag))
# organizerIds = ListField(ReferenceField(Person))
# dataProviderIds = ListField(ReferenceField(Organization))
# grantIds = ListField(ReferenceField(Grant))
# platformId = ReferenceField(ChallengePlatform)
# createdAt = DateTimeField(required=True, default=datetime.datetime.now)
# updatedAt = DateTimeField(required=True, default=datetime.datetime.now)

meta = {'indexes': [
{
'fields': ['$name', '$description', '$topics'],
Expand Down
3 changes: 2 additions & 1 deletion server/openapi_server/dbmodels/challenge_organizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ class ChallengeOrganizer(Document):
def to_dict(self):
doc = self.to_mongo().to_dict()
doc['id'] = str(self.pk)
doc.pop('challengeId', None)
doc['challengeId'] = str(self.challengeId['id'])
# doc.pop('challengeId', None)
return doc
3 changes: 3 additions & 0 deletions server/openapi_server/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from openapi_server.models.challenge_all_of import ChallengeAllOf
from openapi_server.models.challenge_create_request import ChallengeCreateRequest
from openapi_server.models.challenge_create_response import ChallengeCreateResponse
from openapi_server.models.challenge_difficulty import ChallengeDifficulty
from openapi_server.models.challenge_incentive_type import ChallengeIncentiveType
from openapi_server.models.challenge_organizer import ChallengeOrganizer
from openapi_server.models.challenge_organizer_create_request import ChallengeOrganizerCreateRequest
from openapi_server.models.challenge_organizer_create_response import ChallengeOrganizerCreateResponse
Expand All @@ -28,6 +30,7 @@
from openapi_server.models.challenge_sponsor_list import ChallengeSponsorList
from openapi_server.models.challenge_sponsor_role import ChallengeSponsorRole
from openapi_server.models.challenge_status import ChallengeStatus
from openapi_server.models.challenge_submission_type import ChallengeSubmissionType
from openapi_server.models.date_range import DateRange
from openapi_server.models.error import Error
from openapi_server.models.grant import Grant
Expand Down
Loading

0 comments on commit 1047b54

Please sign in to comment.