Skip to content

Commit

Permalink
Merge pull request #1904 from laws-africa/work-in-progress
Browse files Browse the repository at this point in the history
Work in progress
  • Loading branch information
goose-life authored Jan 12, 2024
2 parents 05d42d0 + be10a2f commit 894b88d
Show file tree
Hide file tree
Showing 20 changed files with 607 additions and 430 deletions.
1 change: 1 addition & 0 deletions indigo/bulk_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ def create_or_update(self, row):
def provisionally_save(self, work, old_publication_date=None, new_publication_date=None, old_title=None, new_title=None):
if not self.dry_run:
if not work.pk:
work.work_in_progress = False
work.properties['created_in_bulk'] = True
work.save_with_revision(self.user)
if old_publication_date and new_publication_date:
Expand Down
31 changes: 31 additions & 0 deletions indigo_api/migrations/0029_work_in_progress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 3.2.13 on 2023-12-07 11:18

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('indigo_api', '0028_alter_taxonomytopic_slug'),
]

operations = [
migrations.AddField(
model_name='work',
name='approved_at',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='work',
name='approved_by_user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='work',
name='work_in_progress',
field=models.BooleanField(default=True, help_text='Work in progress, to be approved'),
),
]
24 changes: 24 additions & 0 deletions indigo_api/migrations/0030_backfill_work_in_progress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 3.2.13 on 2023-12-07 10:14
from django.db import migrations


def backfill_work_in_progress(apps, schema_editor):
""" Mark existing works as work_in_progress = False; only new ones should be True by default.
"""
Work = apps.get_model('indigo_api', 'Work')
db_alias = schema_editor.connection.alias

for work in Work.objects.using(db_alias).iterator(100):
work.work_in_progress = False
work.save()


class Migration(migrations.Migration):

dependencies = [
('indigo_api', '0029_work_in_progress'),
]

operations = [
migrations.RunPython(backfill_work_in_progress, migrations.RunPython.noop)
]
37 changes: 36 additions & 1 deletion indigo_api/models/works.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from copy import deepcopy
from actstream import action
from datetime import datetime
from django.db.models import JSONField
from django.db import models
from django.db import models, IntegrityError, transaction
from django.db.models import signals, Q
from django.core.exceptions import ValidationError
from django.contrib.auth.models import User
Expand All @@ -16,6 +17,7 @@
from treebeard.mp_tree import MP_Node

from indigo.plugins import plugins
from indigo_api.signals import work_approved, work_unapproved
from indigo_api.timeline import TimelineCommencementEvent, describe_single_commencement, get_serialized_timeline


Expand Down Expand Up @@ -499,14 +501,22 @@ class Meta:
consolidation_note_override = models.CharField(max_length=1024, null=True, blank=True, help_text='Consolidation note about this particular work, to override consolidation note for place')
disclaimer = models.CharField(max_length=1024, null=True, blank=True, help_text='Disclaimer text about this work')

work_in_progress = models.BooleanField(default=True, help_text="Work in progress, to be approved")

created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
approved_at = models.DateTimeField(null=True, blank=True)

created_by_user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='+')
updated_by_user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='+')
approved_by_user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='+')

objects = WorkManager.from_queryset(WorkQuerySet)()

@property
def approved(self):
return not self.work_in_progress

@property
def locality_code(self):
# Helper to get/set locality using the locality_code, used by the WorkSerializer.
Expand Down Expand Up @@ -638,6 +648,31 @@ def versions(self):
def as_at_date(self):
return self.as_at_date_override or self.place.settings.as_at_date

def approve(self, user, request=None):
self.work_in_progress = False
self.approved_by_user = user
self.approved_at = datetime.now()
self.save_with_revision(user)
action.send(user, verb='approved', action_object=self, place_code=self.place.place_code)
try:
with transaction.atomic():
work_approved.send(sender=self.__class__, work=self, request=request)
except IntegrityError:
pass

def unapprove(self, user):
self.work_in_progress = True
self.approved_by_user = None
self.approved_at = None
self.save_with_revision(user)
action.send(user, verb='unapproved', action_object=self, place_code=self.place.place_code)
work_unapproved.send(sender=self.__class__, work=self)

# unpublish all documents
for document in self.document_set.published():
document.draft = True
document.save_with_revision(user, comment='This document was unpublished because its work was unapproved.')

def __str__(self):
return '%s (%s)' % (self.frbr_uri, self.title)

Expand Down
10 changes: 10 additions & 0 deletions indigo_api/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@
"""


work_approved = Signal()
""" A user has approved a work.
"""


work_unapproved = Signal()
""" A user has unapproved a work (marked it as a work in progress).
"""


document_published = Signal(providing_args=["document", "request"])
""" A user has changed a document from draft to published.
"""
Expand Down
29 changes: 27 additions & 2 deletions indigo_app/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ def __init__(self, country, locality, *args, **kwargs):
self.fields[key] = forms.CharField(label=label, required=False)

if self.instance:
# self.fields['commencing_work'].initial = self.instance.commencing_work
self.fields['frbr_doctype'].initial = self.instance.doctype
self.fields['frbr_subtype'].initial = self.instance.subtype
self.fields['frbr_date'].initial = self.instance.date
Expand Down Expand Up @@ -436,6 +435,7 @@ class WorkFilterForm(forms.Form):
repealed_date_end = forms.DateField(input_formats=['%Y-%m-%d'])

stub = forms.MultipleChoiceField(choices=[('stub', 'Stub'), ('not_stub', 'Not a stub'), ])
work_in_progress = forms.MultipleChoiceField(choices=[('work_in_progress', 'Work in progress'), ('approved', 'Approved')])
status = forms.MultipleChoiceField(choices=[('published', 'published'), ('draft', 'draft')])
sortby = forms.ChoiceField(choices=[('-created_at', '-created_at'), ('created_at', 'created_at'), ('-updated_at', '-updated_at'), ('updated_at', 'updated_at'), ('title', 'title'), ('-title', '-title')])
principal = forms.MultipleChoiceField(required=False, choices=[('principal', 'Principal'), ('not_principal', 'Not Principal')])
Expand Down Expand Up @@ -475,6 +475,17 @@ def filter_queryset(self, queryset, exclude=None):
if self.cleaned_data.get('q'):
queryset = queryset.filter(Q(title__icontains=self.cleaned_data['q']) | Q(frbr_uri__icontains=self.cleaned_data['q']))

# filter by work in progress
if exclude != "work_in_progress":
work_in_progress_filter = self.cleaned_data.get('work_in_progress', [])
work_in_progress_qs = Q()
if "work_in_progress" in work_in_progress_filter:
work_in_progress_qs |= Q(work_in_progress=True)
if "approved" in work_in_progress_filter:
work_in_progress_qs |= Q(work_in_progress=False)

queryset = queryset.filter(work_in_progress_qs)

# filter by stub
if exclude != "stub":
stub_filter = self.cleaned_data.get('stub', [])
Expand Down Expand Up @@ -825,11 +836,12 @@ class WorkBulkActionsForm(forms.Form):
del_taxonomy_topics = forms.ModelMultipleChoiceField(
queryset=TaxonomyTopic.objects.all(),
required=False)
change_work_in_progress = forms.ChoiceField(choices=[('', ''), ('approved', 'Approved'), ('work_in_progress', 'Work in progress')], required=False)

def clean_all_work_pks(self):
return self.cleaned_data.get('all_work_pks').split() or []

def save_changes(self):
def save_changes(self, user, request):
if self.cleaned_data.get('add_taxonomy_topics'):
for work in self.cleaned_data['works']:
work.taxonomy_topics.add(*self.cleaned_data['add_taxonomy_topics'])
Expand All @@ -838,6 +850,19 @@ def save_changes(self):
for work in self.cleaned_data['works']:
work.taxonomy_topics.remove(*self.cleaned_data['del_taxonomy_topics'])

if self.cleaned_data.get('change_work_in_progress'):
if self.cleaned_data['change_work_in_progress'] == 'approved':
for work in self.cleaned_data['works']:
# only save if it's changed
if work.work_in_progress:
work.approve(user, request)

else:
for work in self.cleaned_data['works']:
# only save if it's changed
if work.approved:
work.unapprove(user)


class WorkChooserForm(forms.Form):
country = forms.ModelChoiceField(queryset=Country.objects)
Expand Down
4 changes: 4 additions & 0 deletions indigo_app/static/stylesheets/_works.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,7 @@
overflow-y: auto;
}
}

.bg-work-in-progress {
background-color: lighten($warning, 45%);
}
37 changes: 18 additions & 19 deletions indigo_app/templates/indigo_api/_work_commencement_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<div class="mb-3 row">
<label class="col-2 col-form-label">{% trans 'Commenced by' %}</label>
<div class="col">
<div class="col-4">
<input type="hidden" id="{{ form.commencing_work.id_for_label }}" name="{{ form.commencing_work.html_name }}" value="{{ form.commencing_work.value|default:'' }}">
{% if commencing_work %}
<div class="mb-2">
Expand All @@ -15,33 +15,32 @@
<div>
{% url 'work_form_commencement' place.place_code as edit_url %}
<button
class="btn btn-outline-primary"
type="button"
hx-get="{% url 'place_work_chooser' place.place_code %}?submit={{ edit_url|urlencode }}&target={{ "#work-form-commencing-work"|urlencode }}&field=work-commencing_work"
hx-target="#work-commencing-work-modal"
hx-trigger="click"
data-bs-toggle="modal"
data-bs-target="#work-commencing-work-modal"
>{% trans 'Choose work' %}</button>
class="btn btn-outline-primary"
type="button"
hx-get="{% url 'place_work_chooser' place.place_code %}?submit={{ edit_url|urlencode }}&target={{ "#work-form-commencing-work"|urlencode }}&field=work-commencing_work"
hx-target="#work-commencing-work-modal"
hx-trigger="click"
data-bs-toggle="modal"
data-bs-target="#work-commencing-work-modal"
>{% trans 'Choose commencing work' %}</button>

{% if commencing_work %}
<button
class="btn btn-outline-danger ms-2"
hx-get="{{ edit_url }}"
hx-target="#work-form-commencing-work"
>Clear</button>
class="btn btn-outline-danger ms-2"
hx-get="{{ edit_url }}"
hx-target="#work-form-commencing-work"
>{% trans 'Clear' %}</button>
{% endif %}
</div>

<div class="form-text text-muted">{% trans 'The work that gives the commencement date of this work.' %}</div>
</div>
<div class="col-6 form-text text-muted">{% trans 'The work that brings this work into force.' %}</div>
</div>

<div
id="work-commencing-work-modal"
class="modal modal-blur fade"
style="display: none"
tabindex="-1"
id="work-commencing-work-modal"
class="modal modal-blur fade"
style="display: none"
tabindex="-1"
>
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">Loading...</div>
Expand Down
3 changes: 3 additions & 0 deletions indigo_app/templates/indigo_api/_work_info_badges.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@
{% elif not work.commencement_date %}
<span class="badge text-bg-info">{% trans 'commencement date unknown' %}</span>
{% endif %}
{% if work.work_in_progress %}
<span class="badge text-bg-warning">{% trans 'work in progress' %}</span>
{% endif %}
41 changes: 20 additions & 21 deletions indigo_app/templates/indigo_api/_work_parent_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<div class="mb-3 row">
<label class="col-2 col-form-label">{% trans 'Primary work' %}</label>
<div class="col">
<div class="col-4">
<input type="hidden" id="{{ form.parent_work.id_for_label }}" name="{{ form.parent_work.html_name }}" value="{{ form.parent_work.value|default:'' }}">
{% if work.parent_work %}
<div class="mb-2">
Expand All @@ -15,35 +15,34 @@
<div>
{% url 'work_form_parent' place.place_code as edit_url %}
<button
class="btn btn-outline-primary"
type="button"
hx-get="{% url 'place_work_chooser' place.place_code %}?submit={{ edit_url|urlencode }}&target={{ "#work-form-parent"|urlencode }}&field=work-parent_work"
hx-target="#work-parent-modal"
hx-trigger="click"
data-bs-toggle="modal"
data-bs-target="#work-parent-modal"
>{% trans 'Choose parent work' %}</button>
class="btn btn-outline-primary"
type="button"
hx-get="{% url 'place_work_chooser' place.place_code %}?submit={{ edit_url|urlencode }}&target={{ "#work-form-parent"|urlencode }}&field=work-parent_work"
hx-target="#work-parent-modal"
hx-trigger="click"
data-bs-toggle="modal"
data-bs-target="#work-parent-modal"
>{% trans 'Choose primary work' %}</button>

{% if work.parent_work %}
<button
class="btn btn-outline-danger ms-2"
hx-get="{{ edit_url }}"
hx-target="#work-form-parent"
>Clear</button>
class="btn btn-outline-danger ms-2"
hx-get="{{ edit_url }}"
hx-target="#work-form-parent"
>{% trans 'Clear' %}</button>
{% endif %}
</div>
</div>
<div class="col-6 form-text text-muted">
{% trans 'The primary work for regulations is the Act in terms of which they were promulgated.' %}
</div>
</div>

<p class="form-text text-muted">
{% trans 'The primary work for regulations and notices is the primary Act.' %}
</p>

<div
id="work-parent-modal"
class="modal modal-blur fade"
style="display: none"
tabindex="-1"
id="work-parent-modal"
class="modal modal-blur fade"
style="display: none"
tabindex="-1"
>
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">Loading...</div>
Expand Down
Loading

0 comments on commit 894b88d

Please sign in to comment.