Skip to content

Commit

Permalink
Merge pull request #42 from animorphcoop/dedicated-prompt-act
Browse files Browse the repository at this point in the history
Polls improvements in river flow
  • Loading branch information
nektdev authored Oct 21, 2024
2 parents efce717 + 43434bd commit 034bbc3
Show file tree
Hide file tree
Showing 34 changed files with 461 additions and 433 deletions.
24 changes: 16 additions & 8 deletions .github/workflows/.build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,36 @@ jobs:
with:
node-version: '16.x'

- name: Install Node.js Dependencies
run: npm install

- name: Build JavaScript
run: npm run build

- name: Setup Python
uses: actions/setup-python@v1
with:
python-version: 3.9
python-version: 3.10.15

- name: Install packages
run: sudo apt-get install -y redis-tools redis-server binutils libproj-dev gdal-bin

- name: Verify that redis is up
run: redis-cli ping

- name: Checkout code
uses: actions/checkout@v2

- name: Copy requirements files
run: |
mkdir -p ${GITHUB_WORKSPACE}/sfs/
cp prod/requirements.txt ${GITHUB_WORKSPACE}/sfs/
cp dev/requirements_dev.txt ${GITHUB_WORKSPACE}/sfs/
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
cd sfs
pip install -r requirements.txt
pip install -r requirements_dev.txt
npm install && npm run build
- name: Create log directory with sudo
run: sudo mkdir -p /home/app/sfs/logs/

- name: Run Tests
env:
Expand All @@ -63,4 +71,4 @@ jobs:
run: |
python manage.py migrate --no-input
python manage.py collectstatic --no-input
pytest tests
pytest tests
8 changes: 5 additions & 3 deletions apps/core/management/commands/clear_resources.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from django.core.management.base import BaseCommand
from resources.models import CaseStudy, HowTo, ResourceTag, CustomTag
from resources.models import CaseStudy, CustomTag, HowTo, ResourceTag
from taggit.models import Tag


class Command(BaseCommand):
help = 'Clears all HowTo and CaseStudy entries from the database'
help = "Clears all HowTo and CaseStudy entries from the database"

def handle(self, *args, **options):
# clear tags
Expand All @@ -16,4 +16,6 @@ def handle(self, *args, **options):
HowTo.objects.all().delete()
CaseStudy.objects.all().delete()
ResourceTag.objects.all().delete()
self.stdout.write(self.style.SUCCESS('Successfully cleared HowTo and CaseStudy entries'))
self.stdout.write(
self.style.SUCCESS("Successfully cleared HowTo and CaseStudy entries")
)
12 changes: 2 additions & 10 deletions apps/core/management/commands/load_devdata.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import json
from io import BytesIO
import os

from allauth.account.admin import EmailAddress
from area.models import Area, PostCode
from django.contrib.gis.geos import Point
from django.core.files.images import ImageFile
from django.core.management.base import BaseCommand
from PIL import Image as PillowImage
from resources.models import CaseStudy, HowTo
from userauth.models import CustomUser, Organisation, UserAvatar
from wagtail.images.models import Image
from wagtail.rich_text import RichText
from django.contrib.contenttypes.models import ContentType
from taggit.models import Tag

DATA_DIR = "dev/autoupload/"


def add_organisations(data):
for org_data in data:
try:
Expand Down Expand Up @@ -111,4 +103,4 @@ def handle(self, *args, **options):

add_areas(data["Areas"])
add_organisations(data["Organisations"])
add_users(data["Users"])
add_users(data["Users"])
75 changes: 52 additions & 23 deletions apps/core/management/commands/load_resources.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import json
from io import BytesIO
import os
from io import BytesIO

from django.contrib.gis.geos import Point
from django.core.files.images import ImageFile
from django.core.management.base import BaseCommand
from django.contrib.gis.geos import Point
from PIL import Image as PillowImage
from resources.models import CaseStudy, HowTo, CustomTag
from resources.models import CaseStudy, HowTo
from wagtail.images.models import Image
from wagtail.rich_text import RichText

DATA_DIR = "dev/autoupload/"


class Command(BaseCommand):
help = 'Adds HowTo and CaseStudy entries from the database'
help = "Adds HowTo and CaseStudy entries from the database"

def add_arguments(self, parser):
parser.add_argument('resource_file', type=str, help='Path to the resources JSON file')
parser.add_argument(
"resource_file", type=str, help="Path to the resources JSON file"
)

def handle(self, *args, **options):
resource_file = options['resource_file']
with open(resource_file, 'r') as file:
resource_file = options["resource_file"]
with open(resource_file, "r") as file:
resource_data = json.load(file)
self.add_resources(resource_data)

Expand All @@ -35,9 +37,13 @@ def add_resources(self, resource_data):
for new_howto_data in how_to_resources:
try:
# Ensure mandatory fields are not None
if any(key not in new_howto_data or new_howto_data[key] is None for key in
["title", "summary", "link"]):
raise ValueError(f"Missing mandatory field for How To: {new_howto_data}")
if any(
key not in new_howto_data or new_howto_data[key] is None
for key in ["title", "summary", "link"]
):
raise ValueError(
f"Missing mandatory field for How To: {new_howto_data}"
)

# Check if HowTo already exists
try:
Expand All @@ -53,13 +59,17 @@ def add_resources(self, resource_data):
)

# Handle location
if location_data := new_howto_data.get('location'): # Check if location key exists and is not None
if location_data := new_howto_data.get(
"location"
): # Check if location key exists and is not None
lat = float(location_data["lat"])
lng = float(location_data["lng"])
new_howto.location = Point(lng, lat)

# Handle tags
for tag_name in new_howto_data.get("tags", []): # Ensure 'tags' is an empty list if missing
for tag_name in new_howto_data.get(
"tags", []
): # Ensure 'tags' is an empty list if missing
new_howto.tags.add(tag_name)

new_howto.save()
Expand All @@ -69,19 +79,27 @@ def add_resources(self, resource_data):

except Exception as e:
self.stdout.write(
self.style.ERROR(f"Error loading How To '{new_howto_data.get('title', 'Unknown')}': {e}")
self.style.ERROR(
f"Error loading How To '{new_howto_data.get('title', 'Unknown')}': {e}"
)
)

for new_casestudy_data in case_study_resources:
try:
# Ensure mandatory fields are not None
if any(key not in new_casestudy_data or new_casestudy_data[key] is None for key in
["title", "summary", "link", "body"]):
raise ValueError(f"Missing mandatory field for case study: {new_casestudy_data}")
if any(
key not in new_casestudy_data or new_casestudy_data[key] is None
for key in ["title", "summary", "link", "body"]
):
raise ValueError(
f"Missing mandatory field for case study: {new_casestudy_data}"
)

# Check if case study already exists
try:
new_casestudy = CaseStudy.objects.get(title=new_casestudy_data["title"])
new_casestudy = CaseStudy.objects.get(
title=new_casestudy_data["title"]
)
is_new = False
except CaseStudy.DoesNotExist:
is_new = True
Expand All @@ -93,7 +111,9 @@ def add_resources(self, resource_data):
)

# Handle image
if (image_name := new_casestudy_data.get("image")): # Check if image key exists and is not None
if image_name := new_casestudy_data.get(
"image"
): # Check if image key exists and is not None
image_path = os.path.join(DATA_DIR, image_name)
if os.path.exists(image_path): # Check if the image file exists
with open(image_path, "rb") as f:
Expand All @@ -105,10 +125,14 @@ def add_resources(self, resource_data):
new_casestudy.case_study_image = None

if new_casestudy_data.get("body"): # Check if body key exists
new_casestudy.body = [("body_text", {"content": RichText(new_casestudy_data["body"])})]
new_casestudy.body = [
("body_text", {"content": RichText(new_casestudy_data["body"])})
]

# Handle tags
for tag_name in new_casestudy_data.get("tags", []): # Ensure 'tags' is an empty list if missing
for tag_name in new_casestudy_data.get(
"tags", []
): # Ensure 'tags' is an empty list if missing
new_casestudy.tags.add(tag_name)

new_casestudy.save()
Expand All @@ -118,8 +142,13 @@ def add_resources(self, resource_data):

except Exception as e:
self.stdout.write(
self.style.ERROR(f"Error loading case study '{new_casestudy_data.get('title', 'Unknown')}': {e}")
self.style.ERROR(
f"Error loading case study '{new_casestudy_data.get('title', 'Unknown')}': {e}"
)
)

self.stdout.write(
self.style.SUCCESS(f"Added {added_how_tos} HowTo(s) and {added_case_studies} CaseStudy(ies) successfully."))
self.style.SUCCESS(
f"Added {added_how_tos} HowTo(s) and {added_case_studies} CaseStudy(ies) successfully."
)
)
1 change: 0 additions & 1 deletion apps/dashboard/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ def clean(self):
location = cleaned_data.get("location")

if not location:
print("heheh")
raise forms.ValidationError("Location is required.")
# the "location" field gives us a dict with the multiple values
# which we can include directly in our cleaned data here
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def get_weather(postcode: str) -> Tuple[str, str, Union[str, float]]:
)
# catch all for our yet unknown error to be found in the logs
except Exception as e:
print("weather error")
# print("weather error")
print(e)
return (
"[no data]",
Expand Down
3 changes: 1 addition & 2 deletions apps/landing/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,4 @@ def handle_404(request: HttpRequest, exception: Exception = None) -> HttpRespons


def handle_500(request: HttpRequest) -> HttpResponse:
print("500 error handled with redirect")
return HttpResponseRedirect(reverse("resources"))
return render(request, "500.html", status=500)
1 change: 1 addition & 0 deletions apps/messaging/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def send_system_message(
"salmon_envision_poll_available": "messaging/system_messages/salmon_envision_poll_available.html",
"salmon_wizard": "messaging/system_messages/salmon_wizard.html",
"poll_edited": "messaging/system_messages/poll_edited.html",
"poll_not_passed": "messaging/system_messages/poll_not_passed.html",
}[kind],
chat=chat,
context_action=context_action,
Expand Down
18 changes: 18 additions & 0 deletions apps/poll/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,24 @@ def close(self) -> None:
):
river.start_reflect()
river.save()
elif (
topic != "general"
and river.act_stage.money_poll
and river.act_stage.money_poll.passed
and river.act_stage.place_poll
and river.act_stage.place_poll.passed
and river.act_stage.time_poll
and river.act_stage.time_poll.passed
):
river.make_act_general_poll()
else:
river, stage, topic = self.get_poll_context(self)
send_system_message(
stage.get_chat(topic), "poll_not_passed", context_poll=self
)
self.passed = False
self.save()

elif hasattr(self, "multiplechoicepoll"):
from river.models import ReflectStage, River

Expand Down
65 changes: 35 additions & 30 deletions apps/poll/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ def post(self, request: WSGIRequest, uuid: UUID) -> HttpResponse:
poll.check_closed()
if poll.check_closed() and "slug" in request.POST:
# adding 'just_finished' so frontend can refresh, did not want to tamper with request payload
# print(self.get_context_data(uuid=uuid, request=request, just_finished='true'))
return self.render_to_response(
self.get_context_data(
uuid=uuid, request=request, just_finished="true"
Expand Down Expand Up @@ -110,6 +109,7 @@ def get_context_data(
# river slug for htmx to run conditional check if the poll is closed so to trigger refreshing on the frontend
river = poll.river
ctx["slug"] = river.slug
ctx["river_stage"] = river.current_stage
ctx["starters"] = RiverMembership.objects.filter(
river=river, starter=True
).values_list(
Expand Down Expand Up @@ -179,35 +179,40 @@ def get_success_url(self) -> str:


def poll_edit(request: WSGIRequest) -> HttpResponse:
# update description of poll, return new description for htmx
poll = BasePoll.objects.get(uuid=request.POST["poll-uuid"])
if poll.closed:
# update description of the (old) poll, return new description for htmx
old_poll = BasePoll.objects.get(uuid=request.POST["poll-uuid"])
if old_poll.closed:
raise PermissionDenied("poll is closed")

if RiverMembership.objects.get(user=request.user, river=poll.river).starter:
poll.closed = True
poll.save()
river, stage, topic = poll.get_poll_context(poll)
send_system_message(stage.get_chat(topic), "poll_edited", context_poll=poll)
new_poll = SingleChoicePoll.objects.create(
question=poll.question,
description=request.POST["new-description"],
options=poll.options,
expires=poll.expires,
created_by=poll.created_by,
river=poll.river,
river_membership = RiverMembership.objects.get(
user=request.user, river=old_poll.river
)
if not river_membership.starter:
raise PermissionDenied(
"User is not authorised to edit the poll - non-riverstarter"
)
if topic == "general":
stage.general_poll = new_poll
elif topic == "money":
stage.money_poll = new_poll
elif topic == "place":
stage.place_poll = new_poll
elif topic == "time":
stage.time_poll = new_poll
stage.save()
return HttpResponseRedirect(reverse("poll_view", args=[new_poll.uuid]))
else:
# user isn't a river starter for the river the poll appears in
# the ui shouldn't permit this situation
raise PermissionDenied("non-riverstarter user trying to edit a poll")

river, stage, topic = old_poll.get_poll_context(old_poll)
send_system_message(stage.get_chat(topic), "poll_edited", context_poll=old_poll)
new_poll = SingleChoicePoll.objects.create(
question=old_poll.question,
description=request.POST["new-description"],
options=old_poll.options,
expires=old_poll.expires,
created_by=old_poll.created_by,
river=old_poll.river,
)
if topic == "general":
stage.general_poll = new_poll
elif topic == "money":
stage.money_poll = new_poll
elif topic == "place":
stage.place_poll = new_poll
elif topic == "time":
stage.time_poll = new_poll

old_poll.closed = True
old_poll.save()

stage.save()
return HttpResponseRedirect(reverse("poll_view", args=[new_poll.uuid]))
Loading

0 comments on commit 034bbc3

Please sign in to comment.