Browse Source

Merge branch 'develop' into feature/cloudUpload

pull/1779/head
Hironsan 2 years ago
parent
commit
7b9f1ac4fa
19 changed files with 442 additions and 72 deletions
  1. 2
      backend/data_export/celery_tasks.py
  2. 4
      backend/data_export/pipeline/factories.py
  3. 32
      backend/data_export/pipeline/repositories.py
  4. 2
      backend/data_export/pipeline/writers.py
  5. 375
      backend/data_export/tests/test_repositories.py
  6. 4
      backend/examples/serializers.py
  7. 35
      backend/metrics/tests.py
  8. 9
      backend/metrics/views.py
  9. 2
      tools/azure.sh
  10. 5
      tools/create-admin.sh
  11. 4
      tools/create-package.sh
  12. 2
      tools/dev-celery.sh
  13. 9
      tools/dev-django.sh
  14. 1
      tools/dev-nuxt.sh
  15. 6
      tools/heroku.sh
  16. 3
      tools/install-mssql.sh
  17. 2
      tools/prod-celery.sh
  18. 8
      tools/prod-django.sh
  19. 9
      tools/run.sh

2
backend/data_export/celery_tasks.py

@ -13,7 +13,7 @@ logger = get_task_logger(__name__)
@shared_task
def export_dataset(project_id, file_format: str, export_approved=False):
project = get_object_or_404(Project, pk=project_id)
repository = create_repository(project)
repository = create_repository(project, file_format)
writer = create_writer(file_format)(settings.MEDIA_ROOT)
service = ExportApplicationService(repository, writer)
filepath = service.export(export_approved)

4
backend/data_export/pipeline/factories.py

@ -11,8 +11,8 @@ from projects.models import (
)
def create_repository(project):
if getattr(project, "use_relation", False):
def create_repository(project, file_format: str):
if getattr(project, "use_relation", False) and file_format == catalog.JSONLRelation.name:
return repositories.RelationExtractionRepository(project)
mapping = {
DOCUMENT_CLASSIFICATION: repositories.TextClassificationRepository,

32
backend/data_export/pipeline/repositories.py

@ -1,7 +1,7 @@
import abc
import itertools
from collections import defaultdict
from typing import Dict, Iterator, List, Tuple, Union
from typing import Any, Dict, Iterator, List, Tuple
from .data import Record
from examples.models import Example
@ -10,13 +10,12 @@ from projects.models import Project
SpanType = Tuple[int, int, str]
class BaseRepository(abc.ABC):
class BaseRepository:
def __init__(self, project: Project):
self.project = project
@abc.abstractmethod
def list(self, export_approved=False) -> Iterator[Record]:
pass
raisen> <span class="ne">NotImplementedError()
class FileRepository(BaseRepository):
@ -54,7 +53,7 @@ class FileRepository(BaseRepository):
label_per_user[a.user.username].append(a.label.text)
return label_per_user
def reduce_user(self, label_per_user: Dict[str, List]):
def reduce_user(self, label_per_user: Dict[str, Any]):
value = list(itertools.chain(*label_per_user.values()))
return {"all": value}
@ -96,7 +95,7 @@ class TextRepository(BaseRepository):
def label_per_user(self, doc) -> Dict:
raise NotImplementedError()
def reduce_user(self, label_per_user: Dict[str, List]):
def reduce_user(self, label_per_user: Dict[str, Any]):
value = list(itertools.chain(*label_per_user.values()))
return {"all": value}
@ -161,6 +160,14 @@ class RelationExtractionRepository(TextRepository):
label_per_user[user]["entities"] = span
return label_per_user
def reduce_user(self, label_per_user: Dict[str, Any]):
entities = []
relations = []
for user, label in label_per_user.items():
entities.extend(label.get("entities", []))
relations.extend(label.get("relations", []))
return {"all": {"entities": entities, "relations": relations}}
class Seq2seqRepository(TextRepository):
@property
@ -184,7 +191,7 @@ class IntentDetectionSlotFillingRepository(TextRepository):
def label_per_user(self, doc) -> Dict:
category_per_user: Dict[str, List[str]] = defaultdict(list)
span_per_user: Dict[str, List[SpanType]] = defaultdict(list)
label_per_user: Dict[str, Dict[str, Union[List[str], List[SpanType]]]] = defaultdict(dict)
label_per_user: Dict[str, Dict[str, List]] = defaultdict(dict)
for a in doc.categories.all():
category_per_user[a.user.username].append(a.label.text)
for a in doc.spans.all():
@ -193,4 +200,15 @@ class IntentDetectionSlotFillingRepository(TextRepository):
label_per_user[user]["cats"] = cats
for user, span in span_per_user.items():
label_per_user[user]["entities"] = span
for label in label_per_user.values():
label.setdefault("cats", [])
label.setdefault("entities", [])
return label_per_user
def reduce_user(self, label_per_user: Dict[str, Any]):
cats = []
entities = []
for user, label in label_per_user.items():
cats.extend(label.get("cats", []))
entities.extend(label.get("entities", []))
return {"all": {"entities": entities, "cats": cats}}

2
backend/data_export/pipeline/writers.py

@ -35,7 +35,7 @@ class LineWriter(BaseWriter):
for record in records:
filename = os.path.join(self.tmpdir, f"{record.user}.{self.extension}")
if filename not in files:
f = open(filename, mode="a")
f = open(filename, mode="a", encoding="utf-8")
files[filename] = f
f = files[filename]
line = self.create_line(record)

375
backend/data_export/tests/test_repositories.py

@ -3,72 +3,341 @@ import unittest
from model_mommy import mommy
from ..pipeline.repositories import (
FileRepository,
IntentDetectionSlotFillingRepository,
RelationExtractionRepository,
Seq2seqRepository,
SequenceLabelingRepository,
Speech2TextRepository,
TextClassificationRepository,
)
from projects.models import (
DOCUMENT_CLASSIFICATION,
IMAGE_CLASSIFICATION,
INTENT_DETECTION_AND_SLOT_FILLING,
SEQ2SEQ,
SEQUENCE_LABELING,
SPEECH2TEXT,
)
from projects.models import INTENT_DETECTION_AND_SLOT_FILLING, SEQUENCE_LABELING
from projects.tests.utils import prepare_project
class TestCSVWriter(unittest.TestCase):
def setUp(self):
self.project = prepare_project(INTENT_DETECTION_AND_SLOT_FILLING)
class TestRepository(unittest.TestCase):
def assert_records(self, repository, expected):
records = list(repository.list())
self.assertEqual(len(records), len(expected))
for record, expect in zip(records, expected):
self.assertEqual(record.data, expect["data"])
self.assertEqual(record.label, expect["label"])
self.assertEqual(record.user, expect["user"])
class TestTextClassificationRepository(TestRepository):
def prepare_data(self, project):
self.example = mommy.make("Example", project=project.item, text="example")
self.category1 = mommy.make("Category", example=self.example, user=project.admin)
self.category2 = mommy.make("Category", example=self.example, user=project.annotator)
def test_list(self):
example = mommy.make("Example", project=self.project.item, text="example")
category = mommy.make("Category", example=example, user=self.project.admin)
span = mommy.make("Span", example=example, user=self.project.admin, start_offset=0, end_offset=1)
repository = IntentDetectionSlotFillingRepository(self.project.item)
project = prepare_project(DOCUMENT_CLASSIFICATION)
repository = TextClassificationRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": example.text,
"data": self.example.text,
"label": [self.category1.label.text],
"user": project.admin.username,
},
{
"data": self.example.text,
"label": [self.category2.label.text],
"user": project.annotator.username,
},
]
self.assert_records(repository, expected)
def test_list_on_collaborative_annotation(self):
project = prepare_project(DOCUMENT_CLASSIFICATION, collaborative_annotation=True)
repository = TextClassificationRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.text,
"label": [self.category1.label.text, self.category2.label.text],
"user": "all",
}
]
self.assert_records(repository, expected)
class TestSeq2seqRepository(TestRepository):
def prepare_data(self, project):
self.example = mommy.make("Example", project=project.item, text="example")
self.text1 = mommy.make("TextLabel", example=self.example, user=project.admin)
self.text2 = mommy.make("TextLabel", example=self.example, user=project.annotator)
def test_list(self):
project = prepare_project(SEQ2SEQ)
repository = Seq2seqRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.text,
"label": [self.text1.text],
"user": project.admin.username,
},
{
"data": self.example.text,
"label": [self.text2.text],
"user": project.annotator.username,
},
]
self.assert_records(repository, expected)
def test_list_on_collaborative_annotation(self):
project = prepare_project(SEQ2SEQ, collaborative_annotation=True)
repository = Seq2seqRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.text,
"label": [self.text1.text, self.text2.text],
"user": "all",
}
]
self.assert_records(repository, expected)
class TestIntentDetectionSlotFillingRepository(TestRepository):
def prepare_data(self, project):
self.example = mommy.make("Example", project=project.item, text="example")
self.category1 = mommy.make("Category", example=self.example, user=project.admin)
self.category2 = mommy.make("Category", example=self.example, user=project.annotator)
self.span = mommy.make("Span", example=self.example, user=project.admin, start_offset=0, end_offset=1)
def test_list(self):
project = prepare_project(INTENT_DETECTION_AND_SLOT_FILLING)
repository = IntentDetectionSlotFillingRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.text,
"label": {
"cats": [self.category1.label.text],
"entities": [(self.span.start_offset, self.span.end_offset, self.span.label.text)],
},
"user": project.admin.username,
},
{
"data": self.example.text,
"label": {
"cats": [self.category2.label.text],
"entities": [],
},
"user": project.annotator.username,
},
]
self.assert_records(repository, expected)
def test_list_on_collaborative_annotation(self):
project = prepare_project(INTENT_DETECTION_AND_SLOT_FILLING, collaborative_annotation=True)
repository = IntentDetectionSlotFillingRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.text,
"label": {
"cats": [category.label.text],
"entities": [(span.start_offset, span.end_offset, span.label.text)],
"cats": [self.category1.label.text, self.category2.label.text],
"entities": [(self.span.start_offset, self.span.end_offset, self.span.label.text)],
},
"user": "all",
}
]
records = list(repository.list())
self.assertEqual(len(records), len(expected))
for record, expect in zip(records, expected):
self.assertEqual(record.data, expect["data"])
self.assertEqual(record.label["cats"], expect["label"]["cats"])
self.assertEqual(record.label["entities"], expect["label"]["entities"])
class TestRelationExtractionRepository(unittest.TestCase):
def setUp(self):
self.project = prepare_project(SEQUENCE_LABELING, use_relation=True)
def test_label_per_user(self):
from_entity = mommy.make("Span", start_offset=0, end_offset=1, user=self.project.admin)
to_entity = mommy.make(
"Span", start_offset=1, end_offset=2, example=from_entity.example, user=self.project.admin
)
relation = mommy.make(
"Relation", from_id=from_entity, to_id=to_entity, example=from_entity.example, user=self.project.admin
)
repository = RelationExtractionRepository(self.project.item)
expected = {
"admin": {
"entities": [
{
"id": from_entity.id,
"start_offset": from_entity.start_offset,
"end_offset": from_entity.end_offset,
"label": from_entity.label.text,
},
{
"id": to_entity.id,
"start_offset": to_entity.start_offset,
"end_offset": to_entity.end_offset,
"label": to_entity.label.text,
},
],
"relations": [
{"id": relation.id, "from_id": from_entity.id, "to_id": to_entity.id, "type": relation.type.text}
self.assert_records(repository, expected)
class TestSequenceLabelingRepository(TestRepository):
def prepare_data(self, project):
self.example = mommy.make("Example", project=project.item, text="example")
self.span1 = mommy.make("Span", example=self.example, user=project.admin, start_offset=0, end_offset=1)
self.span2 = mommy.make("Span", example=self.example, user=project.annotator, start_offset=1, end_offset=2)
def test_list(self):
project = prepare_project(SEQUENCE_LABELING)
repository = SequenceLabelingRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.text,
"label": [(self.span1.start_offset, self.span1.end_offset, self.span1.label.text)],
"user": project.admin.username,
},
{
"data": self.example.text,
"label": [(self.span2.start_offset, self.span2.end_offset, self.span2.label.text)],
"user": project.annotator.username,
},
]
self.assert_records(repository, expected)
def test_list_on_collaborative_annotation(self):
project = prepare_project(SEQUENCE_LABELING, collaborative_annotation=True)
repository = SequenceLabelingRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.text,
"label": [
(self.span1.start_offset, self.span1.end_offset, self.span1.label.text),
(self.span2.start_offset, self.span2.end_offset, self.span2.label.text),
],
"user": "all",
}
]
self.assert_records(repository, expected)
class TestRelationExtractionRepository(TestRepository):
def test_list(self):
project = prepare_project(SEQUENCE_LABELING, use_relation=True)
example = mommy.make("Example", project=project.item, text="example")
span1 = mommy.make("Span", example=example, user=project.admin, start_offset=0, end_offset=1)
span2 = mommy.make("Span", example=example, user=project.admin, start_offset=1, end_offset=2)
relation = mommy.make("Relation", from_id=span1, to_id=span2, example=example, user=project.admin)
repository = RelationExtractionRepository(project.item)
expected = [
{
"data": example.text,
"label": {
"entities": [
{
"id": span1.id,
"start_offset": span1.start_offset,
"end_offset": span1.end_offset,
"label": span1.label.text,
},
{
"id": span2.id,
"start_offset": span2.start_offset,
"end_offset": span2.end_offset,
"label": span2.label.text,
},
],
"relations": [
{"id": relation.id, "from_id": span1.id, "to_id": span2.id, "type": relation.type.text}
],
},
"user": project.admin.username,
}
]
self.assert_records(repository, expected)
def test_list_on_collaborative_annotation(self):
project = prepare_project(SEQUENCE_LABELING, collaborative_annotation=True, use_relation=True)
example = mommy.make("Example", project=project.item, text="example")
span1 = mommy.make("Span", example=example, user=project.admin, start_offset=0, end_offset=1)
span2 = mommy.make("Span", example=example, user=project.annotator, start_offset=1, end_offset=2)
relation = mommy.make("Relation", from_id=span1, to_id=span2, example=example, user=project.admin)
repository = RelationExtractionRepository(project.item)
expected = [
{
"data": example.text,
"label": {
"entities": [
{
"id": span1.id,
"start_offset": span1.start_offset,
"end_offset": span1.end_offset,
"label": span1.label.text,
},
{
"id": span2.id,
"start_offset": span2.start_offset,
"end_offset": span2.end_offset,
"label": span2.label.text,
},
],
"relations": [
{"id": relation.id, "from_id": span1.id, "to_id": span2.id, "type": relation.type.text}
],
},
"user": "all",
}
]
self.assert_records(repository, expected)
class TestSpeech2TextRepository(TestRepository):
def prepare_data(self, project):
self.example = mommy.make("Example", project=project.item, text="example")
self.text1 = mommy.make("TextLabel", example=self.example, user=project.admin)
self.text2 = mommy.make("TextLabel", example=self.example, user=project.annotator)
def test_list(self):
project = prepare_project(SPEECH2TEXT)
repository = Speech2TextRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.filename,
"label": [self.text1.text],
"user": project.admin.username,
},
{
"data": self.example.filename,
"label": [self.text2.text],
"user": project.annotator.username,
},
]
self.assert_records(repository, expected)
def test_list_on_collaborative_annotation(self):
project = prepare_project(SPEECH2TEXT, collaborative_annotation=True)
repository = Speech2TextRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.filename,
"label": [self.text1.text, self.text2.text],
"user": "all",
}
]
self.assert_records(repository, expected)
class TestFileRepository(TestRepository):
def prepare_data(self, project):
self.example = mommy.make("Example", project=project.item, text="example")
self.category1 = mommy.make("Category", example=self.example, user=project.admin)
self.category2 = mommy.make("Category", example=self.example, user=project.annotator)
def test_list(self):
project = prepare_project(IMAGE_CLASSIFICATION)
repository = FileRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.filename,
"label": [self.category1.label.text],
"user": project.admin.username,
},
{
"data": self.example.filename,
"label": [self.category2.label.text],
"user": project.annotator.username,
},
]
self.assert_records(repository, expected)
def test_list_on_collaborative_annotation(self):
project = prepare_project(IMAGE_CLASSIFICATION, collaborative_annotation=True)
repository = FileRepository(project.item)
self.prepare_data(project)
expected = [
{
"data": self.example.filename,
"label": [self.category1.label.text, self.category2.label.text],
"user": "all",
}
}
actual = repository.label_per_user(from_entity.example)
self.assertDictEqual(actual, expected)
]
self.assert_records(repository, expected)

4
backend/examples/serializers.py

@ -43,5 +43,5 @@ class ExampleSerializer(serializers.ModelSerializer):
class ExampleStateSerializer(serializers.ModelSerializer):
class Meta:
model = ExampleState
fields = ("id", "example", "confirmed_by")
read_only_fields = ("id", "example", "confirmed_by")
fields = ("id", "example", "confirmed_by", "confirmed_at")
read_only_fields = ("id", "example", "confirmed_by", "confirmed_at")

35
backend/metrics/tests.py

@ -28,6 +28,41 @@ class TestMemberProgress(CRUDMixin):
self.assertEqual(response.data, {"total": 1, "progress": expected_progress})
class TestProgressHelper(CRUDMixin):
collaborative_annotation = False
def setUp(self):
self.project = prepare_project(DOCUMENT_CLASSIFICATION, collaborative_annotation=self.collaborative_annotation)
self.example = make_doc(self.project.item)
mommy.make("ExampleState", example=self.example, confirmed_by=self.project.admin)
self.url = reverse(viewname="progress", args=[self.project.item.id])
class TestProgress(TestProgressHelper):
collaborative_annotation = False
def test_fetch_progress(self):
response = self.assert_fetch(self.project.admin, status.HTTP_200_OK)
expected = {"total": 1, "remaining": 0, "complete": 1}
self.assertEqual(response.data, expected)
def test_cannot_affect_others_progress(self):
for member in self.project.staffs:
response = self.assert_fetch(member, status.HTTP_200_OK)
expected = {"total": 1, "remaining": 1, "complete": 0}
self.assertEqual(response.data, expected)
class TestProgressOnCollaborativeAnnotation(TestProgressHelper):
collaborative_annotation = True
def test_fetch_progress(self):
for member in self.project.members:
response = self.assert_fetch(member, status.HTTP_200_OK)
expected = {"total": 1, "remaining": 0, "complete": 1}
self.assertEqual(response.data, expected)
class TestCategoryDistribution(CRUDMixin):
def setUp(self):
self.project = prepare_project(DOCUMENT_CLASSIFICATION)

9
backend/metrics/views.py

@ -1,5 +1,6 @@
import abc
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
@ -8,7 +9,7 @@ from rest_framework.views import APIView
from examples.models import Example, ExampleState
from label_types.models import CategoryType, LabelType, RelationType, SpanType
from labels.models import Category, Label, Relation, Span
from projects.models import Member
from projects.models import Member, Project
from projects.permissions import IsProjectAdmin, IsProjectStaffAndReadOnly
@ -18,7 +19,11 @@ class ProgressAPI(APIView):
def get(self, request, *args, **kwargs):
examples = Example.objects.filter(project=self.kwargs["project_id"]).values("id")
total = examples.count()
complete = ExampleState.objects.count_done(examples, user=self.request.user)
project = get_object_or_404(Project, pk=self.kwargs["project_id"])
if project.collaborative_annotation:
complete = ExampleState.objects.count_done(examples)
else:
complete = ExampleState.objects.count_done(examples, user=self.request.user)
data = {"total": total, "remaining": total - complete, "complete": complete}
return Response(data=data, status=status.HTTP_200_OK)

2
tools/azure.sh

@ -11,6 +11,8 @@ if [[ -z "${DOCCANO_ADMIN_CONTACT_EMAIL}" ]]; then echo "Missing DOCCANO_ADMIN_C
if [[ -z "${DOCCANO_ADMIN_PASSWORD}" ]]; then echo "Missing DOCCANO_ADMIN_PASSWORD environment variable" >&2; exit 1; fi
if ! az account show >/dev/null; then echo "Must be logged into Azure" >&2; exit 2; fi
set -o nounset
az group create \
--location "${DOCCANO_LOCATION}" \
--name "${DOCCANO_RESOURCE_GROUP}"

5
tools/create-admin.sh

@ -1,8 +1,9 @@
#!/usr/bin/env bash
if [[ "$#" -ne 3 ]]; then echo "Usage: $0 <username> <email> <password>" >&2; exit 1; fi
set -o errexit
set -o nounset
if [[ "$#" -ne 3 ]]; then echo "Usage: $0 <username> <email> <password>" >&2; exit 1; fi
python app/manage.py wait_for_db
python app/manage.py migrate

4
tools/create-package.sh

@ -1,4 +1,8 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
mkdir -p backend/client
cd frontend

2
tools/dev-celery.sh

@ -1,5 +1,7 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
cd "/src/backend"

9
tools/dev-django.sh

@ -1,6 +1,13 @@
#!/usr/bin/env bash
set -o errexit
if [[ -z "${ADMIN_USERNAME}" ]]; then echo "Missing ADMIN_USERNAME environment variable" >&2; exit 1; fi
if [[ -z "${ADMIN_PASSWORD}" ]]; then echo "Missing ADMIN_PASSWORD environment variable" >&2; exit 1; fi
if [[ -z "${ADMIN_EMAIL}" ]]; then echo "Missing ADMIN_EMAIL environment variable" >&2; exit 1; fi
set -o nounset
app="/src/backend"
echo "Initializing database"
@ -18,4 +25,4 @@ if [[ -n "${ADMIN_USERNAME}" ]] && [[ -n "${ADMIN_PASSWORD}" ]] && [[ -n "${ADMI
fi
echo "Starting django"
python -u "${app}/manage.py" runserver 0.0.0.0:8000
python -u "${app}/manage.py" runserver ${HOST:-0.0.0.0}:${PORT:-8000}

1
tools/dev-nuxt.sh

@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
root="$(dirname "$0")/.."
app="${root}/frontend"

6
tools/heroku.sh

@ -2,6 +2,12 @@
set -o errexit
if [[ -z "${ADMIN_USER_NAME}" ]]; then echo "Missing ADMIN_USER_NAME environment variable" >&2; exit 1; fi
if [[ -z "${ADMIN_PASSWORD}" ]]; then echo "Missing ADMIN_PASSWORD environment variable" >&2; exit 1; fi
if [[ -z "${ADMIN_CONTACT_EMAIL}" ]]; then echo "Missing ADMIN_CONTACT_EMAIL environment variable" >&2; exit 1; fi
set -o nounset
python /doccano/backend/manage.py migrate
if [ -n "$ADMIN_USER_NAME" ]; then
python /doccano/backend/manage.py create_admin --noinput --username="$ADMIN_USER_NAME" --email="$ADMIN_CONTACT_EMAIL" --password="$ADMIN_PASSWORD"

3
tools/install-mssql.sh

@ -1,5 +1,8 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
# parse arguments
mode="prod"
for opt in "$@"; do

2
tools/prod-celery.sh

@ -1,5 +1,7 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
cd /backend

8
tools/prod-django.sh

@ -2,6 +2,12 @@
set -o errexit
if [[ -z "${ADMIN_USERNAME}" ]]; then echo "Missing ADMIN_USERNAME environment variable" >&2; exit 1; fi
if [[ -z "${ADMIN_PASSWORD}" ]]; then echo "Missing ADMIN_PASSWORD environment variable" >&2; exit 1; fi
if [[ -z "${ADMIN_EMAIL}" ]]; then echo "Missing ADMIN_EMAIL environment variable" >&2; exit 1; fi
set -o nounset
echo "Making staticfiles"
static_dir=staticfiles
if [[ ! -d $static_dir ]] || [[ -z $(ls -A $static_dir) ]]; then
@ -25,4 +31,4 @@ if [[ -n "${ADMIN_USERNAME}" ]] && [[ -n "${ADMIN_PASSWORD}" ]] && [[ -n "${ADMI
fi
echo "Starting django"
gunicorn --bind="0.0.0.0:${PORT:-8000}" --workers="${WORKERS:-4}" config.wsgi --timeout 300
gunicorn --bind="${HOST:-0.0.0.0}:${PORT:-8000}" --workers="${WORKERS:-4}" config.wsgi --timeout 300

9
tools/run.sh

@ -2,6 +2,15 @@
set -o errexit
if [[ -z "${ADMIN_USERNAME}" ]]; then echo "Missing ADMIN_USERNAME environment variable" >&2; exit 1; fi
if [[ -z "${ADMIN_PASSWORD}" ]]; then echo "Missing ADMIN_PASSWORD environment variable" >&2; exit 1; fi
if [[ -z "${ADMIN_EMAIL}" ]]; then echo "Missing ADMIN_EMAIL environment variable" >&2; exit 1; fi
if [[ -z "${PORT}" ]]; then echo "Missing PORT environment variable" >&2; exit 1; fi
if [[ -z "${WORKERS}" ]]; then echo "Missing WORKERS environment variable" >&2; exit 1; fi
if [[ -z "${CELERY_WORKERS}" ]]; then echo "Missing CELERY_WORKERS environment variable" >&2; exit 1; fi
set -o nounset
echo "Making staticfiles"
static_dir=staticfiles
mkdir -p client/dist/static

Loading…
Cancel
Save