Skip to content

Commit

Permalink
refactor: move logic to CodeMetaSchema
Browse files Browse the repository at this point in the history
  • Loading branch information
alee committed Sep 5, 2024
1 parent fd131d6 commit 0636acd
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 102 deletions.
6 changes: 1 addition & 5 deletions django/core/view_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,6 @@ def get_search_queryset(
criteria.update(tags__name__in=[t.lower() for t in tags])
operator = 'and'
"""

"""
Build text search query
"""
if query:
Query.get(query).add_hit()

Expand All @@ -120,6 +116,7 @@ def get_search_queryset(
# filters, query = parse_query_string(query, operator="and")
# criteria.update(filters)

# generate search query from text
query = build_search_query(query)
else:
query = MATCH_ALL
Expand Down Expand Up @@ -176,7 +173,6 @@ def retrieve_with_perms(self, request, *args, **kwargs):


def add_user_retrieve_perms(instance, data, user):
print(user.get_all_permissions())
data["has_change_perm"] = user.has_perm(
f"{instance._meta.app_label}.change_{instance._meta.model_name}",
instance,
Expand Down
10 changes: 5 additions & 5 deletions django/curator/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ def fsck(queryset):
def pretty_print_fsck_results(results):
for id, result in results.items():
release, errors = result
print("ID: {}".format(id))
print("Release: {}".format(release))
print("Errors")
print(f"ID: {id}")
print(f"Release: {release}")
print("Errors:")
for error in errors:
if isinstance(error, FileNotFoundError):
error_msg = "FileNotFoundError({})".format(repr(error.filename))
error_msg = f"FileNotFoundError({error.filename})"
else:
error_msg = error
print(" {}".format(error_msg))
print(f" {error_msg}")
print("\n")


Expand Down
83 changes: 46 additions & 37 deletions django/library/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,14 @@ def to_affiliation_string(cls, afl):
@property
def codemeta_affiliation(self):
"""
FIXME: move to CodeMeta class
For now codemeta affiliations appear to be a single https://schema.org/Organization
"""
if self.json_affiliations:
return self.to_codemeta_affiliation(self.json_affiliations[0])
return CodeMetaSchema.convert_affiliation(self.json_affiliations[0])

@property
def primary_affiliation(self):
return self.json_affiliations[0] if self.json_affiliations else {}

@property
def primary_affiliation_name(self):
Expand All @@ -204,20 +207,6 @@ def primary_affiliation_name(self):
def primary_json_affiliation_name(self):
return self.json_affiliations[0]["name"] if self.json_affiliations else ""

def to_codemeta_affiliation(self, affiliation):
# FIXME: move to CodeMeta class
if affiliation:
return {
# FIXME: may switch to https://schema.org/ResearchOrganization at some point
"@type": "Organization",
"@id": affiliation.get("ror_id"),
"name": affiliation.get("name"),
"url": affiliation.get("url"),
"identifier": affiliation.get("ror_id"),
"sameAs": affiliation.get("ror_id"),
}
return {}

@staticmethod
def from_user(user):
"""
Expand Down Expand Up @@ -274,21 +263,8 @@ def member_profile_url(self):
def get_markdown_link(self):
return f"[{self.get_full_name()}]({self.member_profile_url})"

# FIXME: move to CodeMeta transformer class
def to_codemeta(self):
codemeta = {
"@type": "Person",
# FIXME: Contributor should proxy to User / MemberProfile fields if User is available and given_name and family_name are not set
"givenName": self.given_name,
"familyName": self.family_name,
}
if self.orcid_url:
codemeta["@id"] = self.orcid_url
if self.json_affiliations:
codemeta["affiliation"] = self.codemeta_affiliation
if self.email:
codemeta["email"] = self.email
return codemeta
return CodeMetaSchema.convert_contributor(self)

def get_aggregated_search_fields(self):
return " ".join(
Expand Down Expand Up @@ -1688,6 +1664,7 @@ def get_fs_api(

def add_contributor(self, contributor: Contributor, role=Role.AUTHOR, index=None):
# Check if a ReleaseContributor with the same contributor already exists
logger.debug("Adding contributor with role: %s, %s", contributor, role)
existing_release_contributor = self.codebase_contributors.filter(
contributor=contributor
).first()
Expand Down Expand Up @@ -2596,7 +2573,7 @@ def convert_platforms(self):
def license_url(self):
if self.license:
return self.license.url
return "DEFAULT LICENSE - https://opensource.org/licenses/MIT"
return "DEFAULT LICENSE: https://opensource.org/licenses/MIT"

@property
def descriptions(self):
Expand Down Expand Up @@ -2670,13 +2647,14 @@ def build(cls, codebase_release: CodebaseRelease):

@classmethod
def convert(cls, codebase_release: CodebaseRelease):
"""
Converts the given CodebaseRelease into a Python dictionary.
"""
common_metadata = codebase_release.common_metadata
metadata = {
**cls.INITIAL_METADATA,
# FIXME: inherit fields from common_metadata, look into how to do this
# better, snake_case to camelCase of variable names is also an issue
**common_metadata.to_dict(),
"@id": common_metadata.permanent_url,
"name": common_metadata.name,
"copyrightYear": common_metadata.copyright_year,
"dateCreated": common_metadata.date_created.isoformat(),
"dateModified": common_metadata.date_modified.isoformat(),
Expand All @@ -2693,8 +2671,7 @@ def convert(cls, codebase_release: CodebaseRelease):
metadata.update(codeRepository=common_metadata.code_repository)
if hasattr(common_metadata, "release_notes"):
metadata.update(releaseNotes=common_metadata.release_notes)

return CodeMetaSchema(metadata)
return metadata

@classmethod
def convert_programming_languages(cls, common_metadata: CommonMetadata):
Expand All @@ -2717,10 +2694,42 @@ def to_creative_work(cls, text):
@classmethod
def convert_authors(cls, common_metadata: CommonMetadata):
return [
author.contributor.to_codemeta()
cls.convert_contributor(author.contributor)
for author in common_metadata.release_contributor_authors
]

@classmethod
def convert_ror_affiliation(cls, affiliation: dict):
if affiliation:
return {
# FIXME: may switch to https://schema.org/ResearchOrganization at some point
"@type": "Organization",
"@id": affiliation.get("ror_id"),
"name": affiliation.get("name"),
"url": affiliation.get("url"),
"identifier": affiliation.get("ror_id"),
"sameAs": affiliation.get("ror_id"),
}
return {}

@classmethod
def convert_contributor(cls, contributor: Contributor):
codemeta = {
"@type": "Person",
# FIXME: Contributor should proxy to User / MemberProfile fields if User is available and given_name and family_name are not set
"givenName": contributor.given_name,
"familyName": contributor.family_name,
}
if contributor.orcid_url:
codemeta["@id"] = contributor.orcid_url
if contributor.json_affiliations:
codemeta["affiliation"] = cls.convert_ror_affiliation(
contributor.primary_affiliation
)
if contributor.email:
codemeta["email"] = contributor.email
return codemeta

@classmethod
def convert_target_product(cls, common_metadata: CommonMetadata):
target_product = {
Expand Down
60 changes: 25 additions & 35 deletions django/library/tests/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import io
import logging
import random
from uuid import UUID

Expand All @@ -16,6 +17,9 @@
from library.serializers import CodebaseSerializer


logger = logging.getLogger(__name__)


class CodebaseFactory(ContentModelFactory):
model = Codebase
serializer = CodebaseSerializer
Expand All @@ -32,6 +36,9 @@ def get_default_data(self):
"uuid": uuid,
"identifier": str(uuid),
"submitter": self.submitter,
"references_text": "Wilensky, U. (1997). NetLogo Wolf Sheep Predation model.",
"replication_text": "This model is a replication of the NetLogo model.",
"associated_publication_text": "This model is associated with a publication.",
}

def data_for_create_request(self, **overrides):
Expand All @@ -41,6 +48,13 @@ def data_for_create_request(self, **overrides):
del serialized["id"]
return serialized

def create_published_release(self, codebase=None, **overrides):
if codebase is None:
codebase = self.create(**overrides)
release = ReleaseSetup.setUpPublishableDraftRelease(codebase)
release.publish()
return release


class ContributorFactory:
def __init__(self, user):
Expand All @@ -58,9 +72,17 @@ def get_default_data(self, user):
}

def create(self, **overrides) -> Contributor:
kwargs = self.get_default_data(overrides.get("user"))
kwargs.update(overrides)
return Contributor.objects.create(**kwargs)
default_data = self.get_default_data(overrides.get("user"))
default_data.update(overrides)
user = default_data.pop("user")
contributor, created = Contributor.objects.get_or_create(
user=user, defaults=default_data
)
if created:
return contributor
else:
logger.warning("Contributor with user already exists: %s", user)
return contributor


class ReleaseContributorFactory:
Expand All @@ -79,38 +101,6 @@ def create(self, contributor: Contributor, **overrides):
return ReleaseContributor.objects.create(contributor=contributor, **kwargs)


class CodebaseReleaseFactory:
def __init__(self, codebase, submitter=None):
if submitter is None:
submitter = codebase.submitter
self.submitter = submitter
self.codebase = codebase

def get_default_data(self):
return {
"description": "Added rational utility decision making to wolves",
"submitter": self.submitter,
"codebase": self.codebase,
"live": True,
}

def create(self, **defaults) -> CodebaseRelease:
kwargs = self.get_default_data()
kwargs.update(defaults)

codebase = kwargs.pop("codebase")
submitter = kwargs.pop("submitter")

codebase_release = codebase.create_release(submitter=submitter)
for k, v in kwargs.items():
if hasattr(codebase_release, k):
setattr(codebase_release, k, v)
else:
raise KeyError('Key "{}" is not a property of codebase'.format(k))
codebase_release.save()
return codebase_release


class PeerReviewFactory:
def __init__(self, submitter, codebase_release):
self.submitter = submitter
Expand Down
17 changes: 6 additions & 11 deletions django/library/tests/test_codemeta.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,8 @@ def setup(self, data):
Create Codebase and CodebaseRelease
"""
codebase_factory = CodebaseFactory(submitter=self.submitter)
self.codebase = codebase_factory.create()
self.codebase_release = self.codebase.create_release(initialize=False)

# set initial texts used for citation
self.codebase_release.codebase.references_text = ""
self.codebase_release.codebase.replication_text = ""
self.codebase_release.codebase.associated_publication_text = ""
self.codebase_release = codebase_factory.create_published_release()
self.codebase = self.codebase_release.codebase

logger.debug("setup() done.")

Expand All @@ -295,7 +290,7 @@ def add_unique_contributor(self, role):
logger.debug(f"adding unique contributor: {contributor.name}")

# Function under test: CodebaseRelease.add_contributor()
release_contributor = self.codebase_release.add_contributor(contributor, role)
self.codebase_release.add_contributor(contributor, role)
self.codebase_release.save()

# since we are always adding a unique user, the contributors count will always increase by one
Expand Down Expand Up @@ -325,7 +320,7 @@ def add_existing_contributor(self, role):
logger.debug(f"adding existing contributor: {contributor.id}")

# Function under test: CodebaseRelease.add_contributor()
release_contributor = self.codebase_release.add_contributor(contributor, role)
self.codebase_release.add_contributor(contributor, role)
self.codebase_release.save()

# if an existing contributor is added again, but with AUTHOR role -> it will be considered "author" and not "nonauthor"
Expand Down Expand Up @@ -454,7 +449,7 @@ def length_agrees(self):
if citation
]

fresh_codemeta = CodeMetaSchema.build(CommonMetadata(self.codebase_release))
fresh_codemeta = CodeMetaSchema.build(self.codebase_release)
# Extract text from CodeMeta.metadata
real_codemeta_citation_flat_text = [
creative_work["text"]
Expand All @@ -472,7 +467,7 @@ def validate_against_schema(self):
"""
logger.debug("validate_against_schema()")

fresh_codemeta = CodeMetaSchema.build(CommonMetadata(self.codebase_release))
fresh_codemeta = CodeMetaSchema.build(self.codebase_release)
try:
jsonschema.validate(
json.loads(fresh_codemeta.to_json()),
Expand Down
10 changes: 1 addition & 9 deletions django/library/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,20 +414,12 @@ def test_list_files(self):
self.assertEqual(
response.status_code,
expected_status_code,
msg="{} {}".format(repr(user), response.data),
msg=f"{user} {response.data}",
)

def test_delete_file(self):
path_to_foo = pathlib.Path("foo.txt")
api = self.codebase_release.get_fs_api()
print(self.codebase_release)
print(
"CodebaseRelease perm %s"
% self.submitter.has_perm(
"library.delete_codebaserelease", self.codebase_release
)
)

# Unpublished codebase release permissions
response = self.client.delete(
api.get_absolute_url(
Expand Down

0 comments on commit 0636acd

Please sign in to comment.