Skip to content

Commit

Permalink
Merge branch 'production' into issue-5138
Browse files Browse the repository at this point in the history
  • Loading branch information
grantfitzsimmons authored Aug 19, 2024
2 parents 404e701 + 136978e commit 5f41486
Show file tree
Hide file tree
Showing 63 changed files with 2,231 additions and 483 deletions.
133 changes: 82 additions & 51 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,29 @@ on:
push:
pull_request:

env:
REGISTRY_IMAGE: specifyconsortium/specify7-service

jobs:
dockerize:
build:
runs-on: ubuntu-latest
# "push" event is not triggered for forks, so need to listen for
# pull_requests, but only for external ones, so as not to run the action
# twice
if:
github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.fork == true
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Prepare
id: prep
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
DOCKER_IMAGE=specifyconsortium/specify7-service
DOCKER_IMAGE=${{ env.REGISTRY_IMAGE }}
VERSION=noop
if [ "${{ github.event_name }}" = "schedule" ]; then
VERSION=nightly
Expand Down Expand Up @@ -54,53 +58,80 @@ jobs:
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
echo "created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
# Determine the platforms based on the branch
PLATFORMS="linux/amd64"
REF_NAME=$(echo "${GITHUB_REF#refs/*/}")
if [[ $GITHUB_REF == refs/tags/* || "$REF_NAME" = "$DEFAULT_BRANCH" ]]; then
PLATFORMS="linux/amd64,linux/arm64"
elif [[ "$REF_NAME" == *arm ]]; then
PLATFORMS="linux/arm64"
fi
echo "platforms=${PLATFORMS}" >> $GITHUB_ENV
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Cache Docker layers
uses: actions/cache@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to DockerHub
uses: docker/login-action@v2
images: ${{ env.REGISTRY_IMAGE }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true

- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ strategy.job-index }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1

- name: Build and push
uses: docker/build-push-action@v3
merge:
runs-on: ubuntu-latest
needs:
- build
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
context: .
file: ./Dockerfile
build-args: |
BUILD_VERSION=${{ steps.prep.outputs.version }}
GIT_SHA=${{ github.sha }}
push: true
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
tags: ${{ steps.prep.outputs.tags }}
labels: |
org.opencontainers.image.title=${{ github.event.repository.name }}
org.opencontainers.image.description=${{ github.event.repository.description }}
org.opencontainers.image.url=${{ github.event.repository.html_url }}
org.opencontainers.image.source=${{ github.event.repository.clone_url }}
org.opencontainers.image.version=${{ steps.prep.outputs.version }}
org.opencontainers.image.created=${{ steps.prep.outputs.created }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.licenses=${{ github.event.repository.license.spdx_id }}
platforms: ${{ env.platforms }}
images: ${{ env.REGISTRY_IMAGE }}

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
43 changes: 27 additions & 16 deletions specifyweb/barvis/views.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
from django.http import HttpResponse

from sqlalchemy.sql.expression import func, distinct
from django.db.models import Count, Q

from specifyweb.middleware.general import require_GET
from specifyweb.specify.views import login_maybe_required
from specifyweb.specify.filter_by_col import filter_by_collection
from specifyweb.specify.api import toJson

from specifyweb.stored_queries.models import Determination, Taxon
from specifyweb.specify.models import Taxon

from django.db import connection


@require_GET
@login_maybe_required
def taxon_bar(request):
"Returns the data for creating a taxon tiles visualization."
cursor = connection.cursor()
cursor.execute("""
SELECT t.TaxonID,
t.RankID,
t.ParentID,
t.Name,
(SELECT COUNT(*) FROM determination d WHERE t.TaxonID = d.TaxonID AND d.IsCurrent = 1)
FROM taxon t
WHERE t.TaxonTreeDefID = %s
""", [request.specify_collection.discipline.taxontreedef_id])
# "Returns the data for creating a taxon tiles visualization."
# cursor = connection.cursor()
# cursor.execute("""
# SELECT t.TaxonID,
# t.RankID,
# t.ParentID,
# t.Name,
# (SELECT COUNT(*) FROM determination d WHERE t.TaxonID = d.TaxonID AND d.IsCurrent = 1)
# FROM taxon t
# WHERE t.TaxonTreeDefID = %s
# """, [request.specify_collection.discipline.taxontreedef_id])

# Implementing the previous SQL query in Django ORM:
taxons = (
Taxon.objects.annotate(
current_determination_count=Count(
'determinations', filter=Q(determinations__iscurrent=True))
)
.values_list("id", "rankid", "parent_id", "name", "current_determination_count")
)
filtered_taxons = filter_by_collection(taxons, request.specify_collection)
result = toJson(list(filtered_taxons))

# SELECT d.TaxonID, COUNT(DISTINCT d.CollectionObjectID), t.ParentID
# FROM determination d
Expand All @@ -33,7 +44,7 @@ def taxon_bar(request):
# GROUP BY d.TaxonId
# ORDER BY d.TaxonId
# """, [request.specify_collection.id])
result = toJson(cursor.fetchall())
# result = toJson(cursor.fetchall())
# session = Session()
# query = session.query(
# Determination.TaxonID,
Expand Down
2 changes: 1 addition & 1 deletion specifyweb/businessrules/rules/cogtype_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from specifyweb.businessrules.orm_signal_handler import orm_signal_handler
from specifyweb.specify.models import Picklist, Picklistitem

@orm_signal_handler('pre_save', 'CollectionObjectGroupType')
@orm_signal_handler('pre_save', 'Collectionobjectgrouptype')
def cogtype_pre_save(cog_type):

# Ensure the cog_type type is validated by being the picklist.
Expand Down
8 changes: 4 additions & 4 deletions specifyweb/businessrules/rules/cojo_rules.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from specifyweb.businessrules.exceptions import BusinessRuleException
from specifyweb.businessrules.orm_signal_handler import orm_signal_handler
from specifyweb.specify.models import CollectionObjectGroupJoin
from specifyweb.specify.models import Collectionobjectgroupjoin

@orm_signal_handler('pre_save', 'CollectionObjectGroupJoin')
@orm_signal_handler('pre_save', 'Collectionobjectgroupjoin')
def cojo_pre_save(cojo):
# Ensure the both the childcog and childco fields are not null.
if cojo.childcog == None and cojo.childco == None:
Expand All @@ -16,12 +16,12 @@ def cojo_pre_save(cojo):
# So when a record is saved with isPrimary set to True, we need to set all other records with the same parentcog
# to isPrimary = False.
if cojo.isprimary == True:
(CollectionObjectGroupJoin.objects
(Collectionobjectgroupjoin.objects
.filter(parentcog=cojo.parentcog)
.update(isprimary=False))

if cojo.issubstrate == True:
(CollectionObjectGroupJoin.objects
(Collectionobjectgroupjoin.objects
.filter(parentcog=cojo.parentcog)
.update(issubstrate=False))

6 changes: 3 additions & 3 deletions specifyweb/businessrules/tests/test_cog_type.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from specifyweb.businessrules.exceptions import BusinessRuleException
from specifyweb.specify.models import CollectionObjectGroupType, Picklist, Picklistitem
from specifyweb.specify.models import Collectionobjectgrouptype, Picklist, Picklistitem
from specifyweb.specify.tests.test_api import DefaultsSetup
from django.db import transaction

# NOTE: Edit this test when a new COG type rule is decided upon.
class COGTypeTest(DefaultsSetup):
def test_cog_type_select_values(self):
CollectionObjectGroupType.objects.create(name='microscope slide', type='Discrete', collection=self.collection)
Collectionobjectgrouptype.objects.create(name='microscope slide', type='Discrete', collection=self.collection)

with self.assertRaises(BusinessRuleException), transaction.atomic():
CollectionObjectGroupType.objects.create(name='whole rock', type='Burrito', collection=self.collection)
Collectionobjectgrouptype.objects.create(name='whole rock', type='Burrito', collection=self.collection)

28 changes: 14 additions & 14 deletions specifyweb/businessrules/tests/test_cojo.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,56 @@
from specifyweb.specify.models import CollectionObjectGroup, CollectionObjectGroupJoin, CollectionObjectGroupType, Picklist, Picklistitem
from specifyweb.specify.models import Collectionobjectgroup, Collectionobjectgroupjoin, Collectionobjectgrouptype, Picklist, Picklistitem
from specifyweb.specify.tests.test_api import DefaultsSetup

class CoJoTest(DefaultsSetup):
def test_cojo_rules_enforcement(self):
cog_type = CollectionObjectGroupType.objects.create(name='microscope slide', type='Discrete', collection=self.collection)
cog_1 = CollectionObjectGroup.objects.create(
cog_type = Collectionobjectgrouptype.objects.create(name='microscope slide', type='Discrete', collection=self.collection)
cog_1 = Collectionobjectgroup.objects.create(
collection=self.collection,
cogtype=cog_type
)
cog_2 = CollectionObjectGroup.objects.create(
cog_2 = Collectionobjectgroup.objects.create(
collection=self.collection,
cogtype=cog_type
)
cog_3 = CollectionObjectGroup.objects.create(
cog_3 = Collectionobjectgroup.objects.create(
collection=self.collection,
cogtype=cog_type
)
cojo_1 = CollectionObjectGroupJoin.objects.create(
cojo_1 = Collectionobjectgroupjoin.objects.create(
parentcog=cog_1,
childcog=cog_2,
isprimary=True,
issubstrate=True
)
cojo_2 = CollectionObjectGroupJoin.objects.create(
cojo_2 = Collectionobjectgroupjoin.objects.create(
parentcog=cog_1,
childcog=cog_3,
isprimary=True,
issubstrate=True
)

cojo_1 = CollectionObjectGroupJoin.objects.get(id=cojo_1.id)
cojo_2 = CollectionObjectGroupJoin.objects.get(id=cojo_2.id)
cojo_1 = Collectionobjectgroupjoin.objects.get(id=cojo_1.id)
cojo_2 = Collectionobjectgroupjoin.objects.get(id=cojo_2.id)

self.assertFalse(cojo_1.isprimary)
self.assertFalse(cojo_1.issubstrate)
self.assertTrue(cojo_2.isprimary)
self.assertTrue(cojo_2.issubstrate)

cog_4 = CollectionObjectGroup.objects.create(
cog_4 = Collectionobjectgroup.objects.create(
collection=self.collection,
cogtype=cog_type
)
cojo_3 = CollectionObjectGroupJoin.objects.create(
cojo_3 = Collectionobjectgroupjoin.objects.create(
parentcog=cog_1,
childcog=cog_4,
isprimary=False,
issubstrate=False
)

cojo_1 = CollectionObjectGroupJoin.objects.get(id=cojo_1.id)
cojo_2 = CollectionObjectGroupJoin.objects.get(id=cojo_2.id)
cojo_3 = CollectionObjectGroupJoin.objects.get(id=cojo_3.id)
cojo_1 = Collectionobjectgroupjoin.objects.get(id=cojo_1.id)
cojo_2 = Collectionobjectgroupjoin.objects.get(id=cojo_2.id)
cojo_3 = Collectionobjectgroupjoin.objects.get(id=cojo_3.id)

self.assertFalse(cojo_1.isprimary)
self.assertFalse(cojo_1.issubstrate)
Expand Down
2 changes: 1 addition & 1 deletion specifyweb/businessrules/tests/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ def test_collection_objects_block_delete(self):
with self.assertRaises(ProtectedError):
self.collection.delete()

models.CollectionObjectType.objects.filter(collection=self.collection).delete()
models.Collectionobjecttype.objects.filter(collection=self.collection).delete()
models.Collectionobject.objects.filter(collection=self.collection).delete()
self.collection.delete()
4 changes: 2 additions & 2 deletions specifyweb/businessrules/tests/test_collectionobject.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from specifyweb.specify.models import Collection, Collectionobject, CollectionObjectType
from specifyweb.specify.models import Collection, Collectionobject, Collectionobjecttype
from specifyweb.specify.tests.test_api import ApiTests
from ..exceptions import BusinessRuleException

Expand All @@ -15,7 +15,7 @@ def test_catalog_number_unique_in_collection(self):
catalognumber=self.collectionobjects[0].catalognumber + 'foo')

def test_default_collectionobjecttype(self):
default_type = CollectionObjectType.objects.create(
default_type = Collectionobjecttype.objects.create(
name="default type",
collection=self.collection,
taxontreedef=self.discipline.taxontreedef
Expand Down
Loading

0 comments on commit 5f41486

Please sign in to comment.