Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Public api #60

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
3 changes: 1 addition & 2 deletions kerckhoff/packages/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ def create_version(
updated_package_item_titles: List[str],
):
"""Creates new PackageVersion object

Arguments:
user {User} -- User object, required argument
package_version {PackageVersion} -- the package version to be added
Expand Down Expand Up @@ -300,4 +299,4 @@ def create_from_google_drive_item(
mime_type=google_drive_file.mimeType,
)
pi.save()
return pi
return pi
81 changes: 81 additions & 0 deletions kerckhoff/packages/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os
from dataclasses import field
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you meant to import this. I'm guessing it was added by the IDE

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used it for string manipulation.

from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator

Expand Down Expand Up @@ -172,3 +174,82 @@ def get_version_data(self, obj: Package):
class Meta(PackageSerializer.Meta):
fields = PackageSerializer.Meta.fields + ("version_data",)
read_only_fields = PackageSerializer.Meta.read_only_fields + ("version_data",)

class PackageInfoSerializer(serializers.ModelSerializer):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather this be called PublicPackageSerializer

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

package_set = serializers.StringRelatedField()
latest_version = serializers.StringRelatedField()
description = serializers.SerializerMethodField()
folder_id = serializers.SerializerMethodField()
folder_url = serializers.SerializerMethodField()
metadata = serializers.SerializerMethodField()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just put all the obj.metadata fields into metadata instead of leaving it as an empty dictionary. And remove the ones from the top level e.g. google drive folder id and folder url

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

data = serializers.SerializerMethodField()
cached_article_preview = serializers.SerializerMethodField()

def get_description(self, obj: Package):
obj.fetch_cache()
versionSerializer = PackageVersionSerializer(obj.get_version(obj.latest_version.id_num))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some error handling in case no versions have been created yet. Right now it will just crash the server.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this serializer should be able to return something for packages even if they have no version, because the list packages endpoint should show every package even if no version has been created

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return versionSerializer.data["version_description"]

def get_folder_id(self, obj: Package):
return obj.metadata["google_drive"]["folder_id"]

def get_folder_url(self, obj: Package):
return obj.metadata["google_drive"]["folder_url"]

def get_metadata(self, obj: Package):
return {}

def get_data(self, obj: Package):
obj.fetch_cache()
package_items = obj.get_version(obj.latest_version.id_num).packageitem_set.all()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok when I wrote this line a few years ago I didn't realize that obj.latest_version.packageitem_set.all() is good enough.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this one, again please add handling for the case where no versions have been created yet

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

img_urls = {}
supported_image_types = [".jpg", ".jpeg", ".png"]
for file in package_items:
file_ext = os.path.splitext(file.file_name)[-1]
if(file.file_name == "article.aml"):
aml_data = file.data["content_rich"]["data"]
if(file_ext in supported_image_types):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't seem to have included the images into the JSON data. I think you should either get rid of this code, or just put them into the JSON as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept these code because I guessed we would include images somewhere later, but I just deleted these for now as you asked.

# Don't worry about images for now
img_urls[file.file_name] = file.data["src_large"]
return {"article": aml_data}

def get_cached_article_preview(self, obj: Package):
obj.fetch_cache()
cached = obj.cached
for item in cached:
if(item["title"] == "article.aml"):
cached_article_preview = item["content_plain"]["raw"]
return cached_article_preview


class Meta:
model = Package
fields = (
"slug",
"description",
"folder_id",
"folder_url",
"metadata",
"data",
"cached_article_preview",
"last_fetched_date",
"package_set",
"latest_version",

)
read_only_fields = (
"description",
"folder_id",
"folder_url",
"metadata",
"data",
"cached_article_preview",
"last_fetched_date",
"package_set",
"latest_version",
)
validators = [
UniqueTogetherValidator(
queryset=Package.objects.all(), fields=("slug", "package_set")
)
]
43 changes: 40 additions & 3 deletions kerckhoff/packages/views.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
from multiprocessing import log_to_stderr
from typing import List
import os
import json
from importlib_metadata import packages_distributions
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bunch of unused imports here. I think again it's from the IDE

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed extra imports.

from rest_framework import mixins, viewsets, filters
from rest_framework.permissions import IsAuthenticated
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from rest_framework.decorators import action
from rest_framework.serializers import Serializer
from rest_framework.response import Response

from kerckhoff.integrations.serializers import IntegrationSerializer
from .tasks import sync_gdrive_task

from .models import PackageSet, Package
from .models import PackageSet, Package, PackageVersion, PackageItem
from .serializers import (
PackageSetSerializer,
PackageSerializer,
RetrievePackageSerializer,
PackageVersionSerializer,
CreatePackageVersionSerializer,
PackageSetDetailedSerializer,
PackageItemSerializer,
PackageInfoSerializer
)


Expand Down Expand Up @@ -80,7 +87,7 @@ class PackageViewSet(
):
"""
Updates and retrieves packages
"""
"""
def get_queryset(self):
return Package.objects.filter(package_set__slug=self.kwargs["package_set_slug"])

Expand Down Expand Up @@ -150,3 +157,33 @@ def perform_create(self, serializer):
lookup_value_regex = slug_with_dots
filter_backends = (filters.OrderingFilter,)
ordering_fields = ("slug", "last_fetched_date", "created_at", "updated_at")



# Public Package View set for External site Kerckhoff API

# mixins.ListModelMixin list out all packages in package set
# mixins.RetrieveModelMixin retrieves specific/individual package within the package set
class PublicPackageViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin):
"""
List and retrieve packages for external site
"""

# Retrieve only the packages from the package set that has the same name/ slug as the defined package set name/slug in the url
def get_queryset(self):
# return package_set
return Package.objects.filter(package_set__slug=self.kwargs["package_set_slug"])


serializer_class = PackageInfoSerializer
permission_classes = (IsAuthenticatedOrReadOnly,)
# set slug as the lookup field so that we look up for packages in the package set with the same slug
lookup_field = "slug"
# verifies if the url slug is a valid slug and matches our valid slug format defined at the top of this file
lookup_value_regex = slug_with_dots






45 changes: 40 additions & 5 deletions kerckhoff/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
PackageSetCreateAndListViewSet,
PackageViewSet,
PackageCreateAndListViewSet,
PublicPackageViewSet
)
from .comments.views import CommentViewSet
from .integrations.views import IntegrationOAuthView
Expand All @@ -28,16 +29,47 @@
router.register(r"package-sets", PackageSetViewSet)
router.register(r"package-sets", PackageSetCreateAndListViewSet)

package_set_router = NestedSimpleRouter(router, r"package-sets", lookup="package_set")
# API Router for Public External Site
public_package_set_router = NestedSimpleRouter(
router,
r"package-sets",
lookup="package_set")

public_package_set_router.register(
r"packages",
PublicPackageViewSet,
base_name="public-package"
)



# To Internal Kerckhoff Frontend Site
package_set_router = NestedSimpleRouter(
router,
r"package-sets",
lookup="package_set")

package_set_router.register(
r"packages", PackageViewSet, base_name="package-sets_packages"
r"packages",
PackageViewSet,
base_name="package-generate_packages"
)

package_set_router.register(
r"packages", PackageCreateAndListViewSet, base_name="package-sets_packages"
r"packages",
PackageCreateAndListViewSet,
base_name="package-sets_packages"
)

package_router = NestedSimpleRouter(package_set_router, r"packages", lookup="package")
package_router.register(r"comments", CommentViewSet, base_name="comments")
package_router = NestedSimpleRouter(
package_set_router,
r"packages",
lookup="package")

package_router.register(
r"comments",
CommentViewSet,
base_name="comments")

schema_view = get_schema_view(
openapi.Info(
Expand Down Expand Up @@ -73,6 +105,9 @@
path("api/v1/", include(router.urls)),
path("api/v1/", include(package_set_router.urls)),
path("api/v1/", include(package_router.urls)),
# path for external site api
path("api/v1/public/",include(public_package_set_router.urls)),

path("api/v1/integrations/", IntegrationOAuthView.as_view()),
path("api-oauth/", include(auth_urlpatterns)),
path("api-token-auth/", views.obtain_auth_token),
Expand Down