Skip to content

Commit

Permalink
Merge pull request #358 from GateNLP/dev
Browse files Browse the repository at this point in the history
Release v2.1.0
  • Loading branch information
twinkarma authored May 5, 2023
2 parents 427821e + 65881e0 commit f9a1a99
Show file tree
Hide file tree
Showing 136 changed files with 11,679 additions and 55,118 deletions.
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
frontend/node_modules
frontend/node_modules
frontend/templates
4 changes: 2 additions & 2 deletions .github/workflows/backendfrontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ jobs:
- name: Install postgresql conda packages
run: conda install -y -c conda-forge postgresql=14.*

- name: Use Node.js v14
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '14'
node-version: '18'

- name: Install npm packages
run: npm install
Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
run: |
cat CHANGELOG.md | perl -e '
BEGIN {
$myversion = $ARGV[0];
$myversion = $ENV{"GITHUB_REF_NAME"};
$myversion =~ s/^v//;
}
while(<STDIN>) {
Expand All @@ -27,15 +27,14 @@ jobs:
} elsif($flag) {
print;
}
}' -- {{ github.ref_name }} > release-changelog.md
}' > release-changelog.md
# Fail if the changelog is empty, i.e. there is no changelog for this release
[ -s release-changelog.md ]
- name: Create release artifacts
run: |
IMAGE_TAG="{{ github.ref_name }}"
sed "s/DEFAULT_IMAGE_TAG=latest/DEFAULT_IMAGE_TAG=${IMAGE_TAG#v}/" install/get-teamware.sh > ./get-teamware.sh
sed "s/DEFAULT_IMAGE_TAG=latest/DEFAULT_IMAGE_TAG=${GITHUB_REF_NAME#v}/" install/get-teamware.sh > ./get-teamware.sh
tar cvzf install.tar.gz README.md docker-compose*.yml generate-docker-env.sh create-django-db.sh nginx custom-policies Caddyfile
- name: Create release
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ on:
push:
branches:
- dev
pull_request:
branches:
- dev
jobs:
build-and-deploy:
runs-on: ubuntu-latest
Expand All @@ -17,17 +20,18 @@ jobs:
python-version: 3.9
- name: Install python dependencies 🐍
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Use Node.js v14
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '14'
node-version: '18'
- name: Install and Build 🔧
run: |
npm install
npm run install:docs
npm run build:docs
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4
if: ${{ github.event_name == 'push' }}
with:
branch: docs-gh-pages # The branch the action should deploy to.
folder: docs/site/gate-teamware # The folder the action should deploy.
2 changes: 1 addition & 1 deletion .github/workflows/image-build-integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 18

- name: Run integration tests with cypress
uses: cypress-io/github-action@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/integration-tests-cypress-record.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 18

- name: Run integration tests with cypress
uses: cypress-io/github-action@v4
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ node_modules
/dist

/static
frontend/static

# Ignore auto-generated documentation site content
docs/site/

# Vue generated html template
base-vue.html

#Cypress
cypress/screenshots
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@

### Fixed

## [2.1.0] 2023-05-03

### Added
- Radio buttons and checkbox widgets can optionally have a per-choice helptext tooltip ([#329](https://github.com/GateNLP/gate-teamware/pull/329))

### Changed
- Frontend build chain now uses [Vite](https://vitejs.dev/) ([#342](https://github.com/GateNLP/gate-teamware/pull/342))
- Navbar and footer are now more responsive ([#339](https://github.com/GateNLP/gate-teamware/pull/339))
- Node version upgraded to 18 ([#357](https://github.com/GateNLP/gate-teamware/pull/357))
- Python and Node base images upgraded to used Bullseye Debian ([#357](https://github.com/GateNLP/gate-teamware/pull/357))

### Fixed
- Footer no longer covers over content ([#339](https://github.com/GateNLP/gate-teamware/pull/339))
- Maximum annotations per document limit is no longer erroneously enforced on training and test documents ([#355](https://github.com/GateNLP/gate-teamware/pull/355))
- Release workflow fixed ([#341](https://github.com/GateNLP/gate-teamware/pull/341))

## [2.0.0] 2023-04-13
### Added
- Isolate documentation build chain ([#326](https://github.com/GateNLP/gate-teamware/pull/326))
Expand Down
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ repository-code: https://github.com/GateNLP/gate-teamware
title: GATE Teamware
type: software
url: https://gatenlp.github.io/gate-teamware/
version: 2.0.0
version: 2.1.0
23 changes: 11 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
# Node build currently only works on amd64
FROM --platform=linux/amd64 node:14-buster-slim as nodebuilder
RUN mkdir /app/
WORKDIR /app/
COPY package.json package-lock.json ./
COPY frontend/package.json frontend/package-lock.json ./frontend/
# only need to do the node build once, even if we're building multi-arch
FROM --platform=$BUILDPLATFORM node:18-bullseye-slim as nodebuilder
COPY package.json package-lock.json /app/
COPY frontend/package.json frontend/package-lock.json /app/frontend/
WORKDIR /app
RUN npm install --unsafe-perm --only=production --no-optional
COPY frontend/ ./frontend/
COPY frontend/ /app/frontend/
RUN npm run build


FROM python:3.9-slim-buster AS backend
FROM python:3.9-slim-bullseye AS backend
ARG TARGETARCH
ENV PYTHONUNBUFFERED 1
RUN apt-get --allow-releaseinfo-change update && \
Expand All @@ -31,14 +30,14 @@ COPY --chown=gate:gate examples/ ./examples/
COPY --chown=gate:gate teamware/ ./teamware/
COPY --chown=gate:gate backend/ ./backend
COPY --chown=gate:gate frontend/ ./frontend/
COPY --chown=gate:gate --from=nodebuilder /app/frontend/templates/base-vue.html ./backend/templates/
COPY --chown=gate:gate --from=nodebuilder /app/frontend/templates/index.html ./backend/templates/
ENTRYPOINT [ "/app/run-server.sh" ]


FROM nginx:stable-alpine as frontend
COPY nginx/health.conf.template /etc/nginx/templates/
COPY --from=nodebuilder /app/frontend/static /usr/share/nginx/html/static
COPY --from=nodebuilder /app/frontend/public/static /usr/share/nginx/html/static
COPY --from=nodebuilder /app/frontend/dist/static /usr/share/nginx/html/static
ENV HEALTH_PORT=8888


Expand All @@ -47,8 +46,8 @@ ENV DJANGO_SETTINGS_MODULE teamware.settings.test
WORKDIR /app/
USER root
RUN apt-get update && \
apt-get -y install curl gnupg xvfb
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
apt-get -y install curl gnupg xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
RUN apt-get -y install nodejs
USER gate:gate
COPY requirements-dev.txt .
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.0
2.1.0
17 changes: 13 additions & 4 deletions backend/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,12 @@ def get_annotator_annotatable_documents_query(self, user, doc_type=DocumentType.
annotatable_docs = Document.objects.filter(project_id=self.pk, doc_type=doc_type) \
.annotate(num_occupied=occupied_count) \
.annotate(num_user_occupied=user_occupied_count) \
.filter(num_occupied__lt=self.annotations_per_doc, num_user_occupied__lt=1)
.filter(num_user_occupied__lt=1)

if doc_type == DocumentType.ANNOTATION:
# Enforce the max number of annotations per document for ANNOTATION docs only (not
# for TRAINING or TEST, which can be annotated by everyone)
annotatable_docs = annotatable_docs.filter(num_occupied__lt=self.annotations_per_doc)

return annotatable_docs

Expand Down Expand Up @@ -887,7 +892,9 @@ def doc_type_str(self):
raise Exception("Unknown document type")

def user_can_annotate_document(self, user):
""" User must not have completed, pending or rejected the document"""
""" User must not have completed, pending or rejected the document,
and if the document is not a training or test document then it must
not already be fully annotated."""
num_user_annotation_in_doc = self.annotations.filter(
Q(user_id=user.pk, status=Annotation.COMPLETED) |
Q(user_id=user.pk, status=Annotation.PENDING) |
Expand All @@ -897,8 +904,10 @@ def user_can_annotate_document(self, user):
raise RuntimeError(
f"The user {user.username} has more than one annotation ({num_user_annotation_in_doc}) in the document.")

return (num_user_annotation_in_doc < 1 and
self.num_completed_and_pending_annotations < self.project.annotations_per_doc)
return num_user_annotation_in_doc < 1 and (
self.doc_type in (DocumentType.TRAINING, DocumentType.TEST) or
self.num_completed_and_pending_annotations < self.project.annotations_per_doc
)

def num_user_completed_annotations(self, user):
return self.annotations.filter(user_id=user.pk, status=Annotation.COMPLETED).count()
Expand Down
76 changes: 69 additions & 7 deletions backend/tests/test_rpc_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -1332,9 +1332,16 @@ def setUp(self):
self.manager = self.get_default_user()
self.manager_request = self.get_loggedin_request()

self.ann1 = get_user_model().objects.create(username="ann1")
self.ann1_request = self.get_request()
self.ann1_request.user = self.ann1
self.num_annotators = 6
annotator_users = [get_user_model().objects.create(username=f"ann{i}") for i in range(self.num_annotators)]
# self.annotators is a list of (user, request) tuples
self.annotators = []
for ann in annotator_users:
req = self.get_request()
req.user = ann
self.annotators.append((ann, req))

self.ann1, self.ann1_request = self.annotators[0]

self.proj = Project.objects.create(owner=self.manager)
self.proj.annotations_per_doc = 3
Expand Down Expand Up @@ -1594,19 +1601,74 @@ def test_annotation_task_with_test_and_train_auto_pass_fail(self):
self.assertEqual(0, self.proj.num_annotator_task_remaining(self.ann1))


def complete_annotations(self, num_annotations_to_complete, expected_doc_type_str, use_wrong_answer=False):
def test_annotations_per_doc_not_enforced_for_training_or_test(self):
self.proj.has_training_stage = True
self.proj.has_test_stage = True
self.proj.min_test_pass_threshold = 1.0
self.proj.can_annotate_after_passing_training_and_test = True
self.proj.save()

docs_annotated_per_user = []
for (i, (ann_user, _)) in enumerate(self.annotators):
# Add to project
self.assertTrue(add_project_annotator(self.manager_request, self.proj.id, ann_user.username))

# Every annotator should be able to complete every training document, even though
# max annotations per document is less than the total number of annotators
self.assertEqual(self.num_training_docs,
self.complete_annotations(self.num_training_docs, "Training", annotator=i))

# Expect perfect score
self.proj.refresh_from_db()
self.assertEqual(self.num_training_docs,
self.proj.get_annotator_document_score(ann_user, DocumentType.TRAINING))

# Every annotator should be able to complete every test document, even though
# max annotations per document is less than the total number of annotators
self.assertEqual(self.num_test_docs,
self.complete_annotations(self.num_test_docs, "Test", annotator=i))

# Expect perfect score
self.proj.refresh_from_db()
self.assertEqual(self.num_training_docs,
self.proj.get_annotator_document_score(ann_user, DocumentType.TRAINING))

# Now attempt to complete task normally
num_annotated = self.complete_annotations(self.num_docs, "Annotation", annotator=i)
docs_annotated_per_user.append(num_annotated)
self.assertLessEqual(num_annotated, self.proj.max_num_task_per_annotator,
f"Annotator {i} was allowed to annotate too many documents")

# All documents should now be fully annotated
self.assertEqual(sum(docs_annotated_per_user), self.num_docs * self.proj.annotations_per_doc,
"Project was not fully annotated")

# But at least one user must have annotated strictly less than max_num_task_per_annotator,
# since the project was set up such that 20 docs * 3 annotations per doc is less than
# 6 annotators * 12 docs per annotator (60% of the corpus) - this verifies that the max
# annotators per document _does_ apply to the annotation phase
self.assertTrue(any(n < self.proj.max_num_task_per_annotator for n in docs_annotated_per_user),
"All users got their full quota of documents - this is more than 3 annotations per doc")


def complete_annotations(self, num_annotations_to_complete, expected_doc_type_str, *, annotator=0,
use_wrong_answer=False):

answer = "positive"
if use_wrong_answer:
answer = "negative"

ann, ann_req = self.annotators[annotator]

# Expect to get self.num_training_docs tasks
num_completed_tasks = 0
for i in range(num_annotations_to_complete):
task_context = get_annotation_task(self.ann1_request)
task_context = get_annotation_task(ann_req)
if task_context:
self.assertEqual(expected_doc_type_str, task_context["document_type"])
complete_annotation_task(self.ann1_request, task_context["annotation_id"], {"sentiment": answer})
self.assertEqual(expected_doc_type_str, task_context.get("document_type"),
f"Document type does not match in task {task_context!r}, " +
"annotator {ann.username}, document {i}")
complete_annotation_task(ann_req, task_context["annotation_id"], {"sentiment": answer})
num_completed_tasks += 1

return num_completed_tasks
Expand Down
2 changes: 1 addition & 1 deletion backend/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class MainView(View):
The main view of the app (index page)
"""

template_page = "base-vue.html"
template_page = "index.html"


def get(self, request, *args, **kwargs):
Expand Down
23 changes: 23 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const { defineConfig } = require("cypress");

module.exports = defineConfig({
projectId: "q3ozpe",

e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require("./cypress/plugins/index.js")(on, config);
},
baseUrl: "http://localhost:8000",
specPattern: "cypress/e2e/**/*.{js,jsx,ts,tsx}",
},

component: {
devServer: {
framework: "vue",
bundler: "vite",
},
},
fixturesFolder: "examples"
});
4 changes: 0 additions & 4 deletions cypress.json

This file was deleted.

File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ describe('Annotation tests', () => {
cy.contains(annotatePageStr).click()
cy.contains("New project name")
cy.contains("Start annotating").click()

// Check tooltip prompt is visible, hovers aren't supported in cypress, see: https://docs.cypress.io/api/commands/hover
cy.get('.annotation-help-prompt').should('be.visible')

cy.contains("Negative").click()
cy.contains("Submit").click()

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit f9a1a99

Please sign in to comment.