From f64e4ecaf4c7c266c8e675bfe65efa2de442ab89 Mon Sep 17 00:00:00 2001 From: Dag7 Date: Tue, 14 May 2024 20:11:27 +0200 Subject: [PATCH 1/6] feat: random entry --- hhub/views.py | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/hhub/views.py b/hhub/views.py index 053cfbd..0349763 100644 --- a/hhub/views.py +++ b/hhub/views.py @@ -1,4 +1,5 @@ import json +import random from django.core.exceptions import FieldError from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator @@ -78,6 +79,104 @@ def entries_all(request): return JsonResponse(serializer.data, safe=False) +def random_entries(request): + """ + Returns a random entry matching some condition. + + Similar to search + """ + # Parse query params, providing defaults + # Filters + developer = request.GET.get("developer", "") + typetag = request.GET.get("typetag", "") + tags = request.GET.get("tags", "") + platform = request.GET.get("platform", "") + + # Pagination + # Request a specific page + page = request.GET.get("page", "") + # Request a specific number of results (>1,<30) + num_elements = request.GET.get("results", 10) + + # Order and sort + order_by_param = request.GET.get("order_by", "") + sort_by_param = request.GET.get("sort", "") + + # Start by selecting everything + entries = Entry.objects.all() + + # Boundaries for elements per page + # if num_elements <= 1: + # num_elements = 1 + # elif num_elements >= 30: + # num_elements = 30 + + # change 3 to how many random items you want + entries = random.sample(list(entries), int(num_elements)) + + # sort and order + if sort_by_param: + entries = sort_and_order(entries, order_by_param, sort_by_param) + + if developer: + entries = entries.filter(developer=developer) + + if platform: + entries = entries.filter(platform=platform) + + if typetag: + entries = entries.filter(typetag=typetag) + + if tags: + # Read the value of tags as an array of tags separated by commas + tags = tags.split(",") + entries = entries.filter(tags__contains=tags) + + results = len(entries) + + # Prepare paginators and number of results + paginator = Paginator(entries, num_elements) + + # Request the desired page of results + try: + entries = paginator.page(page) + except PageNotAnInteger: + entries = paginator.page(1) + page = 1 + except EmptyPage: + entries = paginator.page(paginator.num_pages) + page = paginator.num_pages + + serializer = EntrySerializer(entries, many=True) + + # Read from disks the manifests of the result entries + json_entries = [] + for entry in entries: + data = open(f"db-sources/{entry.basepath}/{entry.slug}/game.json").read() + json_data = json.loads(data) + # Enrich the manifest with some values available only in the (postgres) database + json_data["basepath"] = entry.basepath + json_entries.append(json_data) + + # Prepare final JSON response + return JsonResponse( + { + # total number of results from the query + "results": results, + # total number of pages + "page_total": paginator.num_pages, + # current request page + "page_current": page, + # number of elements in this page + "page_elements": len(serializer.data), + # array of entries manifests + "entries": json_entries, + }, + # Allow non-dict instances to be passed and serialized + safe=False, + ) + + def search_entries(request): """ Returns every entry matching the conditions given in the query From 3d90d0b607038cf5f523d1f09032a55334afafbb Mon Sep 17 00:00:00 2001 From: Dag7 Date: Tue, 14 May 2024 20:16:11 +0200 Subject: [PATCH 2/6] docs+feat: api random route --- API.md | 22 ++++++++++++++++++++++ hhub/urls.py | 1 + 2 files changed, 23 insertions(+) diff --git a/API.md b/API.md index 6826a7a..6e3b79d 100644 --- a/API.md +++ b/API.md @@ -94,6 +94,28 @@ Every matching is case-insensitive. curl hh3.gbdev.io/api/search?tags=Open Source,RPG&platform=GBC ``` +### GET `/random` + +Return a random entry. + +The following query parameters can be used: + +- `type` (exact matching) +- `developer` (exact matching) +- `platform` (exact matching) +- `tags` (exact matching, comma-separated array e.g. `/search?tags=Open Source,RPG`) + +More than one query parameter can be specified. They will be concatenated in "AND" statements, i.e. `/random?type=homebrew&platform=GBC` will return every Homebrew developed with GBC features. + +Every matching is case-insensitive. + +#### Examples + +```bash +# Get every RPG released as Open Source with Game Boy Color features: +curl hh3.gbdev.io/api/random?tags=Open Source,RPG&platform=GBC +``` + ### Pagination Every result is paginated. These additional query params can be used in any of the previous routes: diff --git a/hhub/urls.py b/hhub/urls.py index 05fdb12..7183874 100644 --- a/hhub/urls.py +++ b/hhub/urls.py @@ -28,6 +28,7 @@ path("all", views.entries_all), path("stats", views.stats), path("search", views.search_entries), + path("random", views.random_entries), ] ), ) From c50cd7cbb59f312cf719761410af8fd8a7214188 Mon Sep 17 00:00:00 2001 From: Dag7 Date: Fri, 17 May 2024 17:20:56 +0200 Subject: [PATCH 3/6] style: random is now inside the search --- hhub/urls.py | 1 - hhub/views.py | 104 ++------------------------------------------------ 2 files changed, 4 insertions(+), 101 deletions(-) diff --git a/hhub/urls.py b/hhub/urls.py index 7183874..05fdb12 100644 --- a/hhub/urls.py +++ b/hhub/urls.py @@ -28,7 +28,6 @@ path("all", views.entries_all), path("stats", views.stats), path("search", views.search_entries), - path("random", views.random_entries), ] ), ) diff --git a/hhub/views.py b/hhub/views.py index 0349763..0caa7fb 100644 --- a/hhub/views.py +++ b/hhub/views.py @@ -1,5 +1,4 @@ import json -import random from django.core.exceptions import FieldError from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator @@ -78,105 +77,6 @@ def entries_all(request): ) return JsonResponse(serializer.data, safe=False) - -def random_entries(request): - """ - Returns a random entry matching some condition. - - Similar to search - """ - # Parse query params, providing defaults - # Filters - developer = request.GET.get("developer", "") - typetag = request.GET.get("typetag", "") - tags = request.GET.get("tags", "") - platform = request.GET.get("platform", "") - - # Pagination - # Request a specific page - page = request.GET.get("page", "") - # Request a specific number of results (>1,<30) - num_elements = request.GET.get("results", 10) - - # Order and sort - order_by_param = request.GET.get("order_by", "") - sort_by_param = request.GET.get("sort", "") - - # Start by selecting everything - entries = Entry.objects.all() - - # Boundaries for elements per page - # if num_elements <= 1: - # num_elements = 1 - # elif num_elements >= 30: - # num_elements = 30 - - # change 3 to how many random items you want - entries = random.sample(list(entries), int(num_elements)) - - # sort and order - if sort_by_param: - entries = sort_and_order(entries, order_by_param, sort_by_param) - - if developer: - entries = entries.filter(developer=developer) - - if platform: - entries = entries.filter(platform=platform) - - if typetag: - entries = entries.filter(typetag=typetag) - - if tags: - # Read the value of tags as an array of tags separated by commas - tags = tags.split(",") - entries = entries.filter(tags__contains=tags) - - results = len(entries) - - # Prepare paginators and number of results - paginator = Paginator(entries, num_elements) - - # Request the desired page of results - try: - entries = paginator.page(page) - except PageNotAnInteger: - entries = paginator.page(1) - page = 1 - except EmptyPage: - entries = paginator.page(paginator.num_pages) - page = paginator.num_pages - - serializer = EntrySerializer(entries, many=True) - - # Read from disks the manifests of the result entries - json_entries = [] - for entry in entries: - data = open(f"db-sources/{entry.basepath}/{entry.slug}/game.json").read() - json_data = json.loads(data) - # Enrich the manifest with some values available only in the (postgres) database - json_data["basepath"] = entry.basepath - json_entries.append(json_data) - - # Prepare final JSON response - return JsonResponse( - { - # total number of results from the query - "results": results, - # total number of pages - "page_total": paginator.num_pages, - # current request page - "page_current": page, - # number of elements in this page - "page_elements": len(serializer.data), - # array of entries manifests - "entries": json_entries, - }, - # Allow non-dict instances to be passed and serialized - safe=False, - ) - - def search_entries(request): """ Returns every entry matching the conditions given in the query @@ -192,6 +92,7 @@ def search_entries(request): tags = request.GET.get("tags", "") platform = request.GET.get("platform", "") text_query = request.GET.get("q", "") + random_query = request.GET.get("random", False) # Pagination # Request a specific page @@ -238,6 +139,9 @@ def search_entries(request): ) results = len(entries) + if random_query: + entries = entries.order_by("?") + # Prepare paginators and number of results paginator = Paginator(entries, num_elements) From 7455da23abb1ffb85fed576fd6b74e5da0c223b4 Mon Sep 17 00:00:00 2001 From: Dag7 Date: Fri, 17 May 2024 17:24:45 +0200 Subject: [PATCH 4/6] docs: random query is now documented --- API.md | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/API.md b/API.md index 6e3b79d..936c953 100644 --- a/API.md +++ b/API.md @@ -76,12 +76,12 @@ Returns every entry in the database. Every entry is represented by its game mani Return every entry in the database matching the given conditions. Every entry is represented by its game manifest. The following query parameters can be used: - - `type` (exact matching) - `developer` (exact matching) - `platform` (exact matching) - `tags` (exact matching, comma-separated array e.g. `/search?tags=Open Source,RPG`) - `title` ("contains" matching, e.g. `/search?title=brick` will return "**Brick**ster" and "**Brick**Breaker") +- `random` (if true, the results will be in a random order) More than one query parameter can be specified. They will be concatenated in "AND" statements, i.e. `/search?type=homebrew&platform=GBC` will return every Homebrew developed with GBC features. @@ -94,26 +94,9 @@ Every matching is case-insensitive. curl hh3.gbdev.io/api/search?tags=Open Source,RPG&platform=GBC ``` -### GET `/random` - -Return a random entry. - -The following query parameters can be used: - -- `type` (exact matching) -- `developer` (exact matching) -- `platform` (exact matching) -- `tags` (exact matching, comma-separated array e.g. `/search?tags=Open Source,RPG`) - -More than one query parameter can be specified. They will be concatenated in "AND" statements, i.e. `/random?type=homebrew&platform=GBC` will return every Homebrew developed with GBC features. - -Every matching is case-insensitive. - -#### Examples - ```bash -# Get every RPG released as Open Source with Game Boy Color features: -curl hh3.gbdev.io/api/random?tags=Open Source,RPG&platform=GBC +# Get random Game Boy Color games: +curl hh3.gbdev.io/api/search?random=true&platform=GBC ``` ### Pagination From 3005c1b791701c85455ebf38efcbe5ed4d5af57b Mon Sep 17 00:00:00 2001 From: Dag7 Date: Fri, 17 May 2024 17:25:39 +0200 Subject: [PATCH 5/6] fix: page current now returns an integer number --- hhub/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hhub/views.py b/hhub/views.py index 0caa7fb..800d009 100644 --- a/hhub/views.py +++ b/hhub/views.py @@ -174,7 +174,7 @@ def search_entries(request): # total number of pages "page_total": paginator.num_pages, # current request page - "page_current": page, + "page_current": int(page), # number of elements in this page "page_elements": len(serializer.data), # array of entries manifests From 5a2fe1c53490a5fd59d4bb8c44769811547a71a2 Mon Sep 17 00:00:00 2001 From: Antonio Vivace Date: Fri, 17 May 2024 22:15:42 +0100 Subject: [PATCH 6/6] Update API.md --- API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/API.md b/API.md index 936c953..5b01cc3 100644 --- a/API.md +++ b/API.md @@ -95,7 +95,7 @@ curl hh3.gbdev.io/api/search?tags=Open Source,RPG&platform=GBC ``` ```bash -# Get random Game Boy Color games: +# Get Game Boy Color games in a random order curl hh3.gbdev.io/api/search?random=true&platform=GBC ```