From 262c9b93b84c410c7ea9edd120c4f5f2c7cc927e Mon Sep 17 00:00:00 2001 From: nekton Date: Sat, 12 Oct 2024 16:19:25 +0100 Subject: [PATCH 1/7] improving resource loading and clearing --- README.md | 8 +- .../management/commands/clear_resources.py | 11 ++ apps/core/management/commands/load_devdata.py | 136 +++++++------ .../management/commands/load_resources.py | 187 ++++++++++-------- apps/resources/models.py | 2 +- 5 files changed, 200 insertions(+), 144 deletions(-) create mode 100644 apps/core/management/commands/clear_resources.py diff --git a/README.md b/README.md index 9090831a..c87b1fb0 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ npm run dev This will start a server that’s only serving static files. -Now your development instance (given you have started docker containers) is up and running! To see the app locally go to **http://127.0.0.1:9000** +Now your development instance (given you have started docker containers) is up and running! To see the app locally go to **http://127.0.0.1:9000** or **http://localhost:9000/** (0.0.0.0 is not going to render styles) #### Building assets for production @@ -176,6 +176,12 @@ Or Resources docker compose -f dev/docker-compose.yaml exec app python3 manage.py load_resources dev/autoupload/resources.json ``` +You can remove resources with the following command +```sh +docker compose -f dev/docker-compose.yaml exec app python3 manage.py clear_resources + +``` + Have a look at `devdata/devdata.json` for some user accounts you can log in as. diff --git a/apps/core/management/commands/clear_resources.py b/apps/core/management/commands/clear_resources.py new file mode 100644 index 00000000..82deaec0 --- /dev/null +++ b/apps/core/management/commands/clear_resources.py @@ -0,0 +1,11 @@ +from django.core.management.base import BaseCommand +from resources.models import CaseStudy, HowTo + + +class Command(BaseCommand): + help = 'Clears all HowTo and CaseStudy entries from the database' + + def handle(self, *args, **options): + HowTo.objects.all().delete() + CaseStudy.objects.all().delete() + self.stdout.write(self.style.SUCCESS('Successfully cleared HowTo and CaseStudy entries')) diff --git a/apps/core/management/commands/load_devdata.py b/apps/core/management/commands/load_devdata.py index 90796c2b..bc469171 100644 --- a/apps/core/management/commands/load_devdata.py +++ b/apps/core/management/commands/load_devdata.py @@ -1,5 +1,6 @@ import json from io import BytesIO +import os from allauth.account.admin import EmailAddress from area.models import Area, PostCode @@ -11,82 +12,103 @@ 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 get_or_create_tag(tag_name): + """Get or create a Tag, ensuring uniqueness and handling duplicates gracefully.""" + + + tag, created = Tag.objects.get_or_create(name=tag_name) + return tag def add_resources(resource_data): for new_howto_data in resource_data["How To"]: try: - new_howto = HowTo.objects.get_or_create( - title=new_howto_data["title"], - summary=new_howto_data["summary"], - link=new_howto_data["link"], - location=new_howto_data.get("location", None), - location_exact=new_howto_data.get("location_exact", True), - )[0] - for tag in new_howto_data["tags"]: + # 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}") + + # Check if HowTo already exists + try: + new_howto = HowTo.objects.get(title=new_howto_data["title"]) + is_new = False + except HowTo.DoesNotExist: + is_new = True + new_howto = HowTo( + title=new_howto_data["title"], + summary=new_howto_data["summary"], + link=new_howto_data["link"], + location=new_howto_data.get("location", None), + location_exact=new_howto_data.get("location_exact", True), + ) + + new_howto.save() + + # Handle tags + new_howto.tags.clear() # Clear existing tags + for tag_name in new_howto_data.get("tags", []): # Ensure 'tags' is an empty list if missing + tag = get_or_create_tag(tag_name) new_howto.tags.add(tag) + new_howto.save() + except Exception as e: print( - "could not add howto with definition: " - + str(new_howto_data) - + "\nerror given: " - + repr(e) + f"Error loading How To '{new_howto_data.get('title', 'Unknown')}': {e}" ) + for new_casestudy_data in resource_data["Case Study"]: - if new_casestudy_data["image"] != "": + 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}") + + # Check if case study already exists try: - with open(DATA_DIR + new_casestudy_data["image"], "rb") as f: - pimg = PillowImage.open(DATA_DIR + new_casestudy_data["image"]) + new_casestudy = CaseStudy.objects.get(title=new_casestudy_data["title"]) + is_new = False + except CaseStudy.DoesNotExist: + is_new = True + new_casestudy = CaseStudy( + title=new_casestudy_data["title"], + summary=new_casestudy_data["summary"], + link=new_casestudy_data["link"], + location=new_casestudy_data.get("location", None), + location_exact=new_casestudy_data.get("location_exact", True), + ) + + # Handle image + if new_casestudy_data["image"]: + with open(os.path.join(DATA_DIR, new_casestudy_data["image"]), "rb") as f: img = Image.objects.get_or_create( - file=ImageFile( - BytesIO(f.read()), name=new_casestudy_data["image"] - ), - width=pimg.width, - height=pimg.height, - )[0] - new_casestudy = CaseStudy.objects.get_or_create( - title=new_casestudy_data["title"], - summary=new_casestudy_data["summary"], - case_study_image=img, - link=new_casestudy_data["link"], - location=new_casestudy_data.get("location", None), - location_exact=new_casestudy_data.get("location_exact", True), + file=ImageFile(BytesIO(f.read()), name=new_casestudy_data["image"]) )[0] - new_casestudy.body.append( - ("body_text", {"content": RichText(new_casestudy_data["body"])}) - ) - - for tag in new_casestudy_data["tags"]: - new_casestudy.tags.add(tag) - new_casestudy.save() - - except Exception as e: - print( - "could not load case study image: " - + str(new_casestudy_data["title"]) - + "\nerror given: " - + repr(e) - ) - else: - print(str(new_casestudy_data["title"]) + " has no image") - new_casestudy = CaseStudy.objects.get_or_create( - title=new_casestudy_data["title"], - summary=new_casestudy_data["summary"], - link=new_casestudy_data["link"], - location=new_casestudy_data.get("location", None), - location_exact=new_casestudy_data.get("location_exact", True), - )[0] - new_casestudy.body.append( - ("body_text", {"content": RichText(new_casestudy_data["body"])}) - ) + new_casestudy.case_study_image = img + elif is_new: + new_casestudy.case_study_image = None + + if new_casestudy_data["body"]: + new_casestudy.body = [("body_text", {"content": RichText(new_casestudy_data["body"])})] - for tag in new_casestudy_data["tags"]: + new_casestudy.save() + + # Handle tags + new_casestudy.tags.clear() # Clear existing tags + for tag_name in new_casestudy_data.get("tags", []): # Ensure 'tags' is an empty list if missing + tag = get_or_create_tag(tag_name) new_casestudy.tags.add(tag) + new_casestudy.save() + except Exception as e: + print( + f"Error loading case study '{new_casestudy_data.get('title', 'Unknown')}': {e}" + ) + def add_organisations(data): for org_data in data: @@ -204,4 +226,4 @@ def handle(self, *args, **options): add_areas(data["Areas"]) add_resources(data["Resources"]) add_organisations(data["Organisations"]) - add_users(data["Users"]) + add_users(data["Users"]) \ No newline at end of file diff --git a/apps/core/management/commands/load_resources.py b/apps/core/management/commands/load_resources.py index 913388cb..bc92e445 100644 --- a/apps/core/management/commands/load_resources.py +++ b/apps/core/management/commands/load_resources.py @@ -1,8 +1,9 @@ import json from io import BytesIO - +import os 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 from taggit.models import Tag @@ -12,99 +13,115 @@ DATA_DIR = "dev/autoupload/" -def add_resources(resource_data): - for new_howto_data in resource_data["How To"]: - try: - new_howto = HowTo.objects.get_or_create( - title=new_howto_data["title"], - summary=new_howto_data["summary"], - link=new_howto_data["link"], - location=new_howto_data.get("location", None), - location_exact=new_howto_data.get("location_exact", True), - )[0] - for tag_name in new_howto_data["tags"]: - tag, created = Tag.objects.get_or_create(name=tag_name) - new_howto.tags.add(tag) - new_howto.save() - except Exception as e: - print( - "could not add howto with definition: " - + str(new_howto_data) - + "\nerror given: " - + repr(e) - ) - for new_casestudy_data in resource_data["Case Study"]: - if new_casestudy_data["image"] != "": +class Command(BaseCommand): + 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') + + def handle(self, *args, **options): + resource_file = options['resource_file'] + with open(resource_file, 'r') as file: + resource_data = json.load(file) + self.add_resources(resource_data) + + def get_or_create_tag(self, tag_name): + """Get or create a Tag, ensuring uniqueness and handling duplicates gracefully.""" + tag, created = Tag.objects.get_or_create(name=tag_name) + return tag + + def add_resources(self, resource_data): + resources = resource_data.get("Resources", {}) + how_to_resources = resources.get("How To", []) + case_study_resources = resources.get("Case Study", []) + + for new_howto_data in how_to_resources: try: - with open(DATA_DIR + new_casestudy_data["image"], "rb") as f: - pimg = PillowImage.open(DATA_DIR + new_casestudy_data["image"]) - img = Image.objects.get_or_create( - file=ImageFile( - BytesIO(f.read()), name=new_casestudy_data["image"] - ), - width=pimg.width, - height=pimg.height, - )[0] - new_casestudy = CaseStudy.objects.get_or_create( + # 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}") + + # Check if HowTo already exists + try: + new_howto = HowTo.objects.get(title=new_howto_data["title"]) + is_new = False + except HowTo.DoesNotExist: + is_new = True + new_howto = HowTo( + title=new_howto_data["title"], + summary=new_howto_data["summary"], + link=new_howto_data["link"], + location_exact=new_howto_data.get("location_exact", True), + ) + + # Handle location + 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) + + new_howto.save() + + # Handle tags + new_howto.tags.clear() # Clear existing tags + for tag_name in new_howto_data.get("tags", []): # Ensure 'tags' is an empty list if missing + tag = self.get_or_create_tag(tag_name) + new_howto.tags.add(tag) + + new_howto.save() + + except Exception as e: + self.stdout.write( + 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}") + + # Check if case study already exists + try: + new_casestudy = CaseStudy.objects.get(title=new_casestudy_data["title"]) + is_new = False + except CaseStudy.DoesNotExist: + is_new = True + new_casestudy = CaseStudy( title=new_casestudy_data["title"], summary=new_casestudy_data["summary"], - case_study_image=img, link=new_casestudy_data["link"], - location=new_casestudy_data.get("location", None), location_exact=new_casestudy_data.get("location_exact", True), - )[0] - new_casestudy.body.append( - ("body_text", {"content": RichText(new_casestudy_data["body"])}) ) - for tag_name in new_casestudy_data["tags"]: - tag, created = Tag.objects.get_or_create(name=tag_name) - new_casestudy.tags.add(tag) - new_casestudy.save() + # Handle image + 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: + img = Image.objects.get_or_create( + file=ImageFile(BytesIO(f.read()), name=image_name) + )[0] + new_casestudy.case_study_image = img + elif is_new: + new_casestudy.case_study_image = None - except Exception as e: - print( - "could not load case study image: " - + str(new_casestudy_data["title"]) - + "\nerror given: " - + repr(e) - ) - else: - print(str(new_casestudy_data["title"]) + " has no image") - new_casestudy = CaseStudy.objects.get_or_create( - title=new_casestudy_data["title"], - summary=new_casestudy_data["summary"], - link=new_casestudy_data["link"], - location=new_casestudy_data.get("location", None), - location_exact=new_casestudy_data.get("location_exact", True), - )[0] - new_casestudy.body.append( - ("body_text", {"content": RichText(new_casestudy_data["body"])}) - ) - - for tag_name in new_casestudy_data["tags"]: - tag, created = Tag.objects.get_or_create(name=tag_name) - new_casestudy.tags.add(tag) - new_casestudy.save() + if new_casestudy_data.get("body"): # Check if body key exists + new_casestudy.body = [("body_text", {"content": RichText(new_casestudy_data["body"])})] + new_casestudy.save() -class Command(BaseCommand): - help = "import resource data" + # Handle tags + new_casestudy.tags.clear() # Clear existing tags + for tag_name in new_casestudy_data.get("tags", []): # Ensure 'tags' is an empty list if missing + tag = self.get_or_create_tag(tag_name) + new_casestudy.tags.add(tag) - def add_arguments(self, parser): - parser.add_argument("datafile", nargs="?", type=str) + new_casestudy.save() - def handle(self, *args, **options): - try: - f = open(options["datafile"]) - try: - data = json.load(f) - except: - print("could not parse valid json from " + options["datafile"]) - exit() - f.close() - except: - print("could not read from file: " + options["datafile"]) - exit() - - add_resources(data["Resources"]) + except Exception as e: + self.stdout.write( + self.style.ERROR(f"Error loading case study '{new_casestudy_data.get('title', 'Unknown')}': {e}") + ) diff --git a/apps/resources/models.py b/apps/resources/models.py index 1c50dd1c..bf2435ff 100644 --- a/apps/resources/models.py +++ b/apps/resources/models.py @@ -40,7 +40,7 @@ def relevant_to(self) -> Optional[str]: # do not create Resources! this model is just to inherit specific kinds of resources from -# you can however query Resource.objects, and django will automatically search for anything that inherits from this model. that's pretty neat! +# you can however query Resource.objects, and django will automatically search for anything that inherits from this model. class Resource(ClusterableModel): uuid: models.UUIDField = models.UUIDField(default=uuid4, editable=False) published_on: models.DateTimeField = models.DateTimeField(auto_now_add=True) From 0cdf9bac45c2e1713605199ac9db676e36c915a1 Mon Sep 17 00:00:00 2001 From: nekton Date: Sat, 12 Oct 2024 18:54:16 +0100 Subject: [PATCH 2/7] refactor customtag to allow for Wagtail panel editing --- README.md | 7 +- apps/core/management/commands/load_devdata.py | 94 ---------------- .../management/commands/load_resources.py | 36 +++--- .../remove_duplicate_resource_tags.py | 28 +++++ ...rce_tags_alter_resourcetag_tag_and_more.py | 60 ++++++++++ apps/resources/models.py | 106 +++++++++++------- 6 files changed, 177 insertions(+), 154 deletions(-) create mode 100644 apps/core/management/commands/remove_duplicate_resource_tags.py create mode 100644 apps/resources/migrations/0006_customtag_alter_resource_tags_alter_resourcetag_tag_and_more.py diff --git a/README.md b/README.md index c87b1fb0..64678757 100644 --- a/README.md +++ b/README.md @@ -179,9 +179,14 @@ docker compose -f dev/docker-compose.yaml exec app python3 manage.py load_resour You can remove resources with the following command ```sh docker compose -f dev/docker-compose.yaml exec app python3 manage.py clear_resources - ``` +If there are issues with changing resources through Wagtail, clearing duplicate tags might be needed +```sh +docker compose -f dev/docker-compose.yaml exec app python3 manage.py remove_duplicate_resource_tags +```` + + Have a look at `devdata/devdata.json` for some user accounts you can log in as. diff --git a/apps/core/management/commands/load_devdata.py b/apps/core/management/commands/load_devdata.py index bc469171..a0a107c7 100644 --- a/apps/core/management/commands/load_devdata.py +++ b/apps/core/management/commands/load_devdata.py @@ -17,99 +17,6 @@ DATA_DIR = "dev/autoupload/" -def get_or_create_tag(tag_name): - """Get or create a Tag, ensuring uniqueness and handling duplicates gracefully.""" - - - tag, created = Tag.objects.get_or_create(name=tag_name) - return tag - -def add_resources(resource_data): - for new_howto_data in resource_data["How To"]: - 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}") - - # Check if HowTo already exists - try: - new_howto = HowTo.objects.get(title=new_howto_data["title"]) - is_new = False - except HowTo.DoesNotExist: - is_new = True - new_howto = HowTo( - title=new_howto_data["title"], - summary=new_howto_data["summary"], - link=new_howto_data["link"], - location=new_howto_data.get("location", None), - location_exact=new_howto_data.get("location_exact", True), - ) - - new_howto.save() - - # Handle tags - new_howto.tags.clear() # Clear existing tags - for tag_name in new_howto_data.get("tags", []): # Ensure 'tags' is an empty list if missing - tag = get_or_create_tag(tag_name) - new_howto.tags.add(tag) - - new_howto.save() - - except Exception as e: - print( - f"Error loading How To '{new_howto_data.get('title', 'Unknown')}': {e}" - ) - - for new_casestudy_data in resource_data["Case Study"]: - 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}") - - # Check if case study already exists - try: - new_casestudy = CaseStudy.objects.get(title=new_casestudy_data["title"]) - is_new = False - except CaseStudy.DoesNotExist: - is_new = True - new_casestudy = CaseStudy( - title=new_casestudy_data["title"], - summary=new_casestudy_data["summary"], - link=new_casestudy_data["link"], - location=new_casestudy_data.get("location", None), - location_exact=new_casestudy_data.get("location_exact", True), - ) - - # Handle image - if new_casestudy_data["image"]: - with open(os.path.join(DATA_DIR, new_casestudy_data["image"]), "rb") as f: - img = Image.objects.get_or_create( - file=ImageFile(BytesIO(f.read()), name=new_casestudy_data["image"]) - )[0] - new_casestudy.case_study_image = img - elif is_new: - new_casestudy.case_study_image = None - - if new_casestudy_data["body"]: - new_casestudy.body = [("body_text", {"content": RichText(new_casestudy_data["body"])})] - - new_casestudy.save() - - # Handle tags - new_casestudy.tags.clear() # Clear existing tags - for tag_name in new_casestudy_data.get("tags", []): # Ensure 'tags' is an empty list if missing - tag = get_or_create_tag(tag_name) - new_casestudy.tags.add(tag) - - new_casestudy.save() - - except Exception as e: - print( - f"Error loading case study '{new_casestudy_data.get('title', 'Unknown')}': {e}" - ) - - def add_organisations(data): for org_data in data: try: @@ -224,6 +131,5 @@ def handle(self, *args, **options): # else continue with the rest exit(0) add_areas(data["Areas"]) - add_resources(data["Resources"]) add_organisations(data["Organisations"]) add_users(data["Users"]) \ No newline at end of file diff --git a/apps/core/management/commands/load_resources.py b/apps/core/management/commands/load_resources.py index bc92e445..c1b188b0 100644 --- a/apps/core/management/commands/load_resources.py +++ b/apps/core/management/commands/load_resources.py @@ -4,11 +4,10 @@ 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 -from taggit.models import Tag +from resources.models import CaseStudy, HowTo, CustomTag from wagtail.images.models import Image from wagtail.rich_text import RichText +from django.db import IntegrityError DATA_DIR = "dev/autoupload/" @@ -25,9 +24,16 @@ def handle(self, *args, **options): resource_data = json.load(file) self.add_resources(resource_data) - def get_or_create_tag(self, tag_name): - """Get or create a Tag, ensuring uniqueness and handling duplicates gracefully.""" - tag, created = Tag.objects.get_or_create(name=tag_name) + def get_or_create_custom_tag(self, tag_name): + """Get or create a CustomTag, handling any integrity errors.""" + tag_name_lower = tag_name.lower() + try: + tag, created = CustomTag.objects.get_or_create(name__iexact=tag_name_lower, defaults={"name": tag_name}) + if created: + self.stdout.write(self.style.SUCCESS(f"Tag created: {tag_name}")) + except IntegrityError: + tag = CustomTag.objects.get(name__iexact=tag_name_lower) + self.stdout.write(self.style.WARNING(f"Tag fetched due to IntegrityError: {tag_name}")) return tag def add_resources(self, resource_data): @@ -45,9 +51,7 @@ def add_resources(self, resource_data): # Check if HowTo already exists try: new_howto = HowTo.objects.get(title=new_howto_data["title"]) - is_new = False except HowTo.DoesNotExist: - is_new = True new_howto = HowTo( title=new_howto_data["title"], summary=new_howto_data["summary"], @@ -56,7 +60,7 @@ 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 lat = float(location_data["lat"]) lng = float(location_data["lng"]) new_howto.location = Point(lng, lat) @@ -65,8 +69,8 @@ def add_resources(self, resource_data): # Handle tags new_howto.tags.clear() # Clear existing tags - for tag_name in new_howto_data.get("tags", []): # Ensure 'tags' is an empty list if missing - tag = self.get_or_create_tag(tag_name) + for tag_name in new_howto_data.get("tags", []): # Ensure 'tags' is a list even if missing + tag = self.get_or_create_custom_tag(tag_name) new_howto.tags.add(tag) new_howto.save() @@ -86,9 +90,7 @@ def add_resources(self, resource_data): # Check if case study already exists try: new_casestudy = CaseStudy.objects.get(title=new_casestudy_data["title"]) - is_new = False except CaseStudy.DoesNotExist: - is_new = True new_casestudy = CaseStudy( title=new_casestudy_data["title"], summary=new_casestudy_data["summary"], @@ -97,7 +99,7 @@ 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 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: @@ -105,7 +107,7 @@ def add_resources(self, resource_data): file=ImageFile(BytesIO(f.read()), name=image_name) )[0] new_casestudy.case_study_image = img - elif is_new: + else: new_casestudy.case_study_image = None if new_casestudy_data.get("body"): # Check if body key exists @@ -115,8 +117,8 @@ def add_resources(self, resource_data): # Handle tags new_casestudy.tags.clear() # Clear existing tags - for tag_name in new_casestudy_data.get("tags", []): # Ensure 'tags' is an empty list if missing - tag = self.get_or_create_tag(tag_name) + for tag_name in new_casestudy_data.get("tags", []): # Ensure 'tags' is a list even if missing + tag = self.get_or_create_custom_tag(tag_name) new_casestudy.tags.add(tag) new_casestudy.save() diff --git a/apps/core/management/commands/remove_duplicate_resource_tags.py b/apps/core/management/commands/remove_duplicate_resource_tags.py new file mode 100644 index 00000000..d968ad53 --- /dev/null +++ b/apps/core/management/commands/remove_duplicate_resource_tags.py @@ -0,0 +1,28 @@ +from django.core.management.base import BaseCommand +from django.db import models +from taggit.models import Tag +from resources.models import HowTo, CaseStudy + + +class Command(BaseCommand): + help = 'Remove duplicate tags from the database' + + def handle(self, *args, **kwargs): + duplicate_tags = Tag.objects.values('name').annotate(name_count=models.Count('name')).filter(name_count__gt=1) + + for tag in duplicate_tags: + duplicate_instances = Tag.objects.filter(name=tag['name']).order_by('id') + primary_tag = duplicate_instances.first() + + for duplicate in duplicate_instances[1:]: + for resource in HowTo.objects.filter(tags=duplicate): + resource.tags.remove(duplicate) + resource.tags.add(primary_tag) + + for resource in CaseStudy.objects.filter(tags=duplicate): + resource.tags.remove(duplicate) + resource.tags.add(primary_tag) + + duplicate.delete() + + self.stdout.write(self.style.SUCCESS('Duplicate tags merged successfully.')) diff --git a/apps/resources/migrations/0006_customtag_alter_resource_tags_alter_resourcetag_tag_and_more.py b/apps/resources/migrations/0006_customtag_alter_resource_tags_alter_resourcetag_tag_and_more.py new file mode 100644 index 00000000..ce9714f6 --- /dev/null +++ b/apps/resources/migrations/0006_customtag_alter_resource_tags_alter_resourcetag_tag_and_more.py @@ -0,0 +1,60 @@ +# Generated by Django 4.2.3 on 2024-10-12 17:01 + +from django.db import migrations, models +import django.db.models.deletion +import modelcluster.contrib.taggit + + +class Migration(migrations.Migration): + dependencies = [ + ("taggit", "0005_auto_20220424_2025"), + ("resources", "0005_resource_location_exact"), + ] + + operations = [ + migrations.CreateModel( + name="CustomTag", + fields=[ + ( + "tag_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="taggit.tag", + ), + ), + ], + options={ + "verbose_name": "Tag", + "verbose_name_plural": "Tags", + }, + bases=("taggit.tag",), + ), + migrations.AlterField( + model_name="resource", + name="tags", + field=modelcluster.contrib.taggit.ClusterTaggableManager( + blank=True, + help_text="A comma-separated list of tags.", + through="resources.ResourceTag", + to="resources.CustomTag", + verbose_name="Tags", + ), + ), + migrations.AlterField( + model_name="resourcetag", + name="tag", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(app_label)s_%(class)s_items", + to="resources.customtag", + ), + ), + migrations.AlterUniqueTogether( + name="resourcetag", + unique_together={("content_object", "tag")}, + ), + ] diff --git a/apps/resources/models.py b/apps/resources/models.py index bf2435ff..2d976b5a 100644 --- a/apps/resources/models.py +++ b/apps/resources/models.py @@ -1,16 +1,20 @@ +import json from typing import Optional from uuid import uuid4 from core.utils.tags_declusterer import tag_cluster_to_list + from django.contrib.gis.db.models import PointField +from django.core.exceptions import ValidationError + from django.db import models -from django.db.models.signals import post_save +from django.db.models.signals import pre_save, post_save from django.dispatch import receiver from django.utils.text import slugify from modelcluster.contrib.taggit import ClusterTaggableManager from modelcluster.fields import ParentalKey from modelcluster.models import ClusterableModel -from taggit.models import TaggedItemBase +from taggit.models import Tag as TaggitTag, TaggedItemBase from wagtail.admin.panels import FieldPanel from wagtail.fields import StreamField from wagtailgeowidget.panels import LeafletPanel @@ -18,18 +22,30 @@ from apps.core.utils.slugifier import generate_random_string from apps.streams import blocks +class CustomTag(TaggitTag): + class Meta: + verbose_name = "Tag" + verbose_name_plural = "Tags" + + def save(self, *args, **kwargs): + if CustomTag.objects.filter(name__iexact=self.name).exists(): + raise ValidationError("Tag with this name already exists.") + super().save(*args, **kwargs) class ResourceTag(TaggedItemBase): content_object = ParentalKey( "resources.Resource", on_delete=models.CASCADE, related_name="tagged_items" ) + tag = models.ForeignKey(CustomTag, on_delete=models.CASCADE, related_name="%(app_label)s_%(class)s_items") + class Meta: + unique_together = ("content_object", "tag") class SavedResource(models.Model): - saved_resource: models.ForeignKey = models.ForeignKey( + saved_resource = models.ForeignKey( "resources.Resource", on_delete=models.CASCADE ) - saved_by: models.ForeignKey = models.ForeignKey( + saved_by = models.ForeignKey( "userauth.CustomUser", on_delete=models.CASCADE ) @@ -38,33 +54,16 @@ def relevant_to(self) -> Optional[str]: if self.saved_by and hasattr(self.saved_by, "pk"): return self.saved_by.pk - -# do not create Resources! this model is just to inherit specific kinds of resources from -# you can however query Resource.objects, and django will automatically search for anything that inherits from this model. class Resource(ClusterableModel): - uuid: models.UUIDField = models.UUIDField(default=uuid4, editable=False) - published_on: models.DateTimeField = models.DateTimeField(auto_now_add=True) - edited_on: models.DateTimeField = models.DateTimeField(auto_now=True) - slug: models.SlugField = models.SlugField( - max_length=100, unique=True, editable=False - ) - title: models.CharField = models.CharField( - max_length=50, - blank=False, - null=False, - ) - summary: models.CharField = models.CharField( - max_length=300, - blank=False, - null=False, - ) + uuid = models.UUIDField(default=uuid4, editable=False) + published_on = models.DateTimeField(auto_now_add=True) + edited_on = models.DateTimeField(auto_now=True) + slug = models.SlugField(max_length=100, unique=True, editable=False) + title = models.CharField(max_length=50, blank=False, null=False) + summary = models.CharField(max_length=300, blank=False, null=False) location = PointField(geography=True, srid=4326, blank=True, null=True) location_exact = models.BooleanField(default=True) - link: models.CharField = models.CharField( - max_length=200, - blank=True, - null=True, - ) + link = models.CharField(max_length=200, blank=True, null=True) tags = ClusterTaggableManager(through=ResourceTag, blank=True) @property @@ -73,9 +72,31 @@ def tag_list(self): return self.tags return tag_cluster_to_list(self.tags) - def __str__(self) -> str: + def __str__(self): return f"{self.title}" + def save(self, *args, **kwargs): + if not self.slug: + self.slug = slugify(self.title + '-' + str(uuid4())[:8]) + super(Resource, self).save(*args, **kwargs) # Call the actual save method + + unique_tags = {} + for tag in self.tags.all(): + if tag.name not in unique_tags: + unique_tags[tag.name] = tag + else: + # Avoid dupes by removing duplicates from Resource + self.tags.remove(tag) + + +@receiver(pre_save, sender=Resource) +def handle_pre_save(sender, instance, *args, **kwargs): + unique_tags = {} + for tag in instance.tags.all(): + if tag.name not in unique_tags: + unique_tags[tag.name] = tag + else: + instance.tags.remove(tag) class HowTo(Resource): class Meta: @@ -90,9 +111,8 @@ class Meta: LeafletPanel("location"), ] - class CaseStudy(Resource): - case_study_image: models.ForeignKey = models.ForeignKey( + case_study_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=False, @@ -100,11 +120,8 @@ class CaseStudy(Resource): on_delete=models.SET_NULL, related_name="+", ) - # could be a streamfield body = StreamField( - [ - ("body_text", blocks.RichTextSimpleBlock()), - ], + [("body_text", blocks.RichTextSimpleBlock())], null=True, blank=True, use_json_field=True, @@ -125,22 +142,27 @@ class Meta: verbose_name_plural = "Case Studies" -# SIGNALS -# TODO: Find out 'sender' type -# (unclear what this todo means?) +@receiver(pre_save, sender=Resource) +def handle_pre_save(sender, instance, *args, **kwargs): + unique_tags = {} + for tag in instance.tags.all(): + if tag.name not in unique_tags: + unique_tags[tag.name] = tag + else: + instance.tags.remove(tag) + @receiver(post_save, sender=HowTo) -def add_slug_to_how_to(sender, instance, *args, **kwargs) -> None: +def add_slug_to_how_to(sender, instance, *args, **kwargs): if instance and not instance.slug: slug = slugify(instance.title) random_string = generate_random_string() instance.slug = slug + "-" + random_string instance.save() - @receiver(post_save, sender=CaseStudy) -def add_slug_to_case_study(sender, instance, *args, **kwargs) -> None: +def add_slug_to_case_study(sender, instance, *args, **kwargs): if instance and not instance.slug: slug = slugify(instance.title) random_string = generate_random_string() instance.slug = slug + "-" + random_string - instance.save() + instance.save() \ No newline at end of file From 601a00640e8396c56c4906817603c425aa8dd39f Mon Sep 17 00:00:00 2001 From: nekton Date: Sun, 13 Oct 2024 22:48:18 +0100 Subject: [PATCH 3/7] clearing resource taggit tags important for re-adding tags in case of overlap --- README.md | 2 +- .../management/commands/clear_resources.py | 10 +++- .../management/commands/load_resources.py | 47 +++++++------------ apps/resources/models.py | 5 +- dev/docker-compose.yaml | 4 +- 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 64678757..b2d6273e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Shared Futures Space +# Shared Futures App A project management platform where your community collaborates and gets stuff done. diff --git a/apps/core/management/commands/clear_resources.py b/apps/core/management/commands/clear_resources.py index 82deaec0..e1ad54ba 100644 --- a/apps/core/management/commands/clear_resources.py +++ b/apps/core/management/commands/clear_resources.py @@ -1,11 +1,19 @@ from django.core.management.base import BaseCommand -from resources.models import CaseStudy, HowTo +from resources.models import CaseStudy, HowTo, ResourceTag, CustomTag +from taggit.models import Tag class Command(BaseCommand): help = 'Clears all HowTo and CaseStudy entries from the database' def handle(self, *args, **options): + # clear tags + ResourceTag.objects.all().delete() + CustomTag.objects.all().delete() + Tag.objects.all().delete() + + # remove items HowTo.objects.all().delete() CaseStudy.objects.all().delete() + ResourceTag.objects.all().delete() self.stdout.write(self.style.SUCCESS('Successfully cleared HowTo and CaseStudy entries')) diff --git a/apps/core/management/commands/load_resources.py b/apps/core/management/commands/load_resources.py index c1b188b0..c8d15b16 100644 --- a/apps/core/management/commands/load_resources.py +++ b/apps/core/management/commands/load_resources.py @@ -4,14 +4,13 @@ from django.core.files.images import ImageFile from django.core.management.base import BaseCommand from django.contrib.gis.geos import Point -from resources.models import CaseStudy, HowTo, CustomTag +from PIL import Image as PillowImage +from resources.models import CaseStudy, HowTo, CustomTag, ResourceTag from wagtail.images.models import Image from wagtail.rich_text import RichText -from django.db import IntegrityError DATA_DIR = "dev/autoupload/" - class Command(BaseCommand): help = 'Adds HowTo and CaseStudy entries from the database' @@ -24,17 +23,6 @@ def handle(self, *args, **options): resource_data = json.load(file) self.add_resources(resource_data) - def get_or_create_custom_tag(self, tag_name): - """Get or create a CustomTag, handling any integrity errors.""" - tag_name_lower = tag_name.lower() - try: - tag, created = CustomTag.objects.get_or_create(name__iexact=tag_name_lower, defaults={"name": tag_name}) - if created: - self.stdout.write(self.style.SUCCESS(f"Tag created: {tag_name}")) - except IntegrityError: - tag = CustomTag.objects.get(name__iexact=tag_name_lower) - self.stdout.write(self.style.WARNING(f"Tag fetched due to IntegrityError: {tag_name}")) - return tag def add_resources(self, resource_data): resources = resource_data.get("Resources", {}) @@ -51,7 +39,9 @@ def add_resources(self, resource_data): # Check if HowTo already exists try: new_howto = HowTo.objects.get(title=new_howto_data["title"]) + is_new = False except HowTo.DoesNotExist: + is_new = True new_howto = HowTo( title=new_howto_data["title"], summary=new_howto_data["summary"], @@ -60,21 +50,20 @@ def add_resources(self, resource_data): ) # Handle location - if location_data := new_howto_data.get('location'): # Check if location key exists + 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) - new_howto.save() - # Handle tags - new_howto.tags.clear() # Clear existing tags - for tag_name in new_howto_data.get("tags", []): # Ensure 'tags' is a list even if missing - tag = self.get_or_create_custom_tag(tag_name) - new_howto.tags.add(tag) + + for tag_name in new_howto_data.get("tags", []): # Ensure 'tags' is an empty list if missing + #self.stdout.write(self.style.NOTICE(f"HowTo tag '{tag_name}'.")) + new_howto.tags.add(tag_name) new_howto.save() + except Exception as e: self.stdout.write( self.style.ERROR(f"Error loading How To '{new_howto_data.get('title', 'Unknown')}': {e}") @@ -90,7 +79,9 @@ def add_resources(self, resource_data): # Check if case study already exists try: new_casestudy = CaseStudy.objects.get(title=new_casestudy_data["title"]) + is_new = False except CaseStudy.DoesNotExist: + is_new = True new_casestudy = CaseStudy( title=new_casestudy_data["title"], summary=new_casestudy_data["summary"], @@ -99,7 +90,7 @@ def add_resources(self, resource_data): ) # Handle image - if (image_name := new_casestudy_data.get("image")): # Check if image key exists + 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: @@ -107,23 +98,21 @@ def add_resources(self, resource_data): file=ImageFile(BytesIO(f.read()), name=image_name) )[0] new_casestudy.case_study_image = img - else: + elif is_new: 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.save() # Handle tags - new_casestudy.tags.clear() # Clear existing tags - for tag_name in new_casestudy_data.get("tags", []): # Ensure 'tags' is a list even if missing - tag = self.get_or_create_custom_tag(tag_name) - new_casestudy.tags.add(tag) + for tag_name in new_casestudy_data.get("tags", []): # Ensure 'tags' is an empty list if missing + #self.stdout.write(self.style.NOTICE(f"Tag passed is '{tag_name}'.")) + new_casestudy.tags.add(tag_name) new_casestudy.save() except Exception as e: self.stdout.write( self.style.ERROR(f"Error loading case study '{new_casestudy_data.get('title', 'Unknown')}': {e}") - ) + ) \ No newline at end of file diff --git a/apps/resources/models.py b/apps/resources/models.py index 2d976b5a..68ef7fea 100644 --- a/apps/resources/models.py +++ b/apps/resources/models.py @@ -28,10 +28,11 @@ class Meta: verbose_name_plural = "Tags" def save(self, *args, **kwargs): - if CustomTag.objects.filter(name__iexact=self.name).exists(): - raise ValidationError("Tag with this name already exists.") + if not self.pk and CustomTag.objects.filter(name__iexact=self.name).exists(): + return CustomTag.objects.get(name__iexact=self.name) super().save(*args, **kwargs) + class ResourceTag(TaggedItemBase): content_object = ParentalKey( "resources.Resource", on_delete=models.CASCADE, related_name="tagged_items" diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index 5cdfab76..f7c19379 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -4,8 +4,8 @@ services: context: .. dockerfile: dev/Dockerfile args: - USERID: $USER_ID - GROUPID: $GROUP_ID + USERID: ${USER_ID:-1000} + GROUPID: ${GROUP_ID:-1000} volumes: - ..:/home/app/sfs command: > From e5079370b898664b36455a59d2fa157108e3ee49 Mon Sep 17 00:00:00 2001 From: nekton Date: Sun, 13 Oct 2024 23:21:49 +0100 Subject: [PATCH 4/7] river starter able to continue posting after enough swimmers in the river --- README.md | 6 - apps/core/management/commands/load_devdata.py | 21 - .../management/commands/load_resources.py | 21 +- .../remove_duplicate_resource_tags.py | 28 - dev/autoupload/dev_data.json | 1036 ----------------- templates/messaging/chatbox_snippet.html | 467 ++++---- 6 files changed, 250 insertions(+), 1329 deletions(-) delete mode 100644 apps/core/management/commands/remove_duplicate_resource_tags.py diff --git a/README.md b/README.md index b2d6273e..2c1fa10e 100644 --- a/README.md +++ b/README.md @@ -181,12 +181,6 @@ You can remove resources with the following command docker compose -f dev/docker-compose.yaml exec app python3 manage.py clear_resources ``` -If there are issues with changing resources through Wagtail, clearing duplicate tags might be needed -```sh -docker compose -f dev/docker-compose.yaml exec app python3 manage.py remove_duplicate_resource_tags -```` - - Have a look at `devdata/devdata.json` for some user accounts you can log in as. diff --git a/apps/core/management/commands/load_devdata.py b/apps/core/management/commands/load_devdata.py index a0a107c7..78958ec1 100644 --- a/apps/core/management/commands/load_devdata.py +++ b/apps/core/management/commands/load_devdata.py @@ -32,22 +32,6 @@ def add_organisations(data): ) -def add_avatars(avatars_data): - for avatar_data in avatars_data: - try: - with open(DATA_DIR + avatar_data["avatar"], "rb") as f: - new_avatar = UserAvatar.objects.create() - new_avatar.avatar = ImageFile(f) - new_avatar.save() - except Exception as e: - print( - "could not add avatar with definition: " - + str(avatar_data) - + "\nerror given: " - + repr(e) - ) - - def add_users(users_data): for user_data in users_data: try: @@ -125,11 +109,6 @@ def handle(self, *args, **options): print("could not read from file: " + options["datafile"]) exit() - add_avatars(data["User Avatars"]) - if options["datafile"] == "autoupload/avatars.json": - # if datafile is avatars only, exit with success, - # else continue with the rest - exit(0) add_areas(data["Areas"]) add_organisations(data["Organisations"]) add_users(data["Users"]) \ No newline at end of file diff --git a/apps/core/management/commands/load_resources.py b/apps/core/management/commands/load_resources.py index c8d15b16..1168ce7c 100644 --- a/apps/core/management/commands/load_resources.py +++ b/apps/core/management/commands/load_resources.py @@ -5,12 +5,13 @@ 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, ResourceTag +from resources.models import CaseStudy, HowTo, CustomTag 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' @@ -23,12 +24,14 @@ def handle(self, *args, **options): resource_data = json.load(file) self.add_resources(resource_data) - def add_resources(self, resource_data): resources = resource_data.get("Resources", {}) how_to_resources = resources.get("How To", []) case_study_resources = resources.get("Case Study", []) + added_how_tos = 0 + added_case_studies = 0 + for new_howto_data in how_to_resources: try: # Ensure mandatory fields are not None @@ -56,13 +59,13 @@ def add_resources(self, resource_data): 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 - #self.stdout.write(self.style.NOTICE(f"HowTo tag '{tag_name}'.")) new_howto.tags.add(tag_name) new_howto.save() + if is_new: + added_how_tos += 1 except Exception as e: self.stdout.write( @@ -104,15 +107,19 @@ def add_resources(self, resource_data): if new_casestudy_data.get("body"): # Check if body key exists 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 - #self.stdout.write(self.style.NOTICE(f"Tag passed is '{tag_name}'.")) new_casestudy.tags.add(tag_name) new_casestudy.save() + if is_new: + added_case_studies += 1 + except Exception as e: self.stdout.write( self.style.ERROR(f"Error loading case study '{new_casestudy_data.get('title', 'Unknown')}': {e}") - ) \ No newline at end of file + ) + + self.stdout.write( + self.style.SUCCESS(f"Added {added_how_tos} HowTo(s) and {added_case_studies} CaseStudy(ies) successfully.")) diff --git a/apps/core/management/commands/remove_duplicate_resource_tags.py b/apps/core/management/commands/remove_duplicate_resource_tags.py deleted file mode 100644 index d968ad53..00000000 --- a/apps/core/management/commands/remove_duplicate_resource_tags.py +++ /dev/null @@ -1,28 +0,0 @@ -from django.core.management.base import BaseCommand -from django.db import models -from taggit.models import Tag -from resources.models import HowTo, CaseStudy - - -class Command(BaseCommand): - help = 'Remove duplicate tags from the database' - - def handle(self, *args, **kwargs): - duplicate_tags = Tag.objects.values('name').annotate(name_count=models.Count('name')).filter(name_count__gt=1) - - for tag in duplicate_tags: - duplicate_instances = Tag.objects.filter(name=tag['name']).order_by('id') - primary_tag = duplicate_instances.first() - - for duplicate in duplicate_instances[1:]: - for resource in HowTo.objects.filter(tags=duplicate): - resource.tags.remove(duplicate) - resource.tags.add(primary_tag) - - for resource in CaseStudy.objects.filter(tags=duplicate): - resource.tags.remove(duplicate) - resource.tags.add(primary_tag) - - duplicate.delete() - - self.stdout.write(self.style.SUCCESS('Duplicate tags merged successfully.')) diff --git a/dev/autoupload/dev_data.json b/dev/autoupload/dev_data.json index 7e5fa175..b1c69ed6 100644 --- a/dev/autoupload/dev_data.json +++ b/dev/autoupload/dev_data.json @@ -74,986 +74,6 @@ ] } }, - "Resources": { - "How To": [ - { - "title": "Developing a Business Case", - "summary": "To make a compelling argument about your idea for a project or organisation, it helps to be able to present your rationale from a few angles. You can argue why it is viable using Five Cases Model prepared by HM Treasury.", - "link": "https://www.economy-ni.gov.uk/articles/business-cases", - "tags": [ - "economic", - "planning", - "toolkit", - "guidance" - ], - "location": { - "lat": "54.5996", - "lng": "-5.9213" - }, - "location_exact": false - }, - { - "title": "Starting New Business Advice", - "summary": "Have an idea but not sure how to go about going ahead with it? You can get a support from the Go For It Programme, which provides step-by-step advice and mentoring to guide founders towards the launch and success of their businesses.", - "link": "https://www.goforitni.com/about-go-for-it/", - "tags": [ - "enterprise", - "support", - "advice" - ] - }, - { - "title": "Funding sports projects", - "summary": "The best organisation to turn to for financial support for a project concerned with physical activities is Sport NI. They always have a few funding programmes on the go, which you can apply for!", - "link": "http://www.sportni.net/funding/our-funding-programmes/", - "tags": [ - "culture", - "sports", - "funding" - ] - }, - { - "title": "Starting a Community Group", - "summary": "If you are working with other towards a shared goal but you do not have a formal group, why not start one? It can give you more leverage in advancing your agenda as well as provide practical benefits such as being able to receive funding.", - "link": "https://www.nihe.gov.uk/Community/Community-Involvement/Setting-up-a-community-group", - "tags": [ - "community", - "organising", - "wellbeing" - ] - }, - { - "title": "Planning Your River", - "summary": "This provides a template for project planning and evaluation provided by the Department of Economy.", - "link": "https://www.economy-ni.gov.uk/articles/evaluations", - "tags": [ - "economic", - "planning", - "toolkit", - "guidance" - ] - }, - { - "title": "Starting a Social Enterprise", - "summary": "Programmes and resources for those wanting to start a social enterprise and cooperatives in Northern Ireland.", - "link": "https://communityfoundationni.org/programmes/community-innovators/", - "tags": [ - "community", - "economic", - "enterprise" - ] - }, - { - "title": "Funding", - "summary": "An online resource sharing all information about grants.", - "link": "https://www.grantsonline.org.uk/region-news/northern-ireland/", - "tags": [ - "funding", - "community", - "economic" - ] - }, - { - "title": "Settling into a Community", - "summary": "This resource links you to a Community Welcome Pack to help you settle into your local community.", - "link": "https://www.nihe.gov.uk/Community/Community-Involvement/Community-welcome-packs", - "tags": [ - "community", - "inclusion", - "wellbeing" - ] - }, - { - "title": "Restorative Justice", - "summary": "Northern Ireland Alternatives (NIA) is a government accredited restorative justice programme that aims to promote and develop non-violent community responses to the issues of low-level crime and anti-social behaviour in areas across Northern Ireland. N,E & S Belfast, Greater Shankill and N.Down.", - "link": "https://alternativesrj.co.uk/", - "tags": [ - "community", - "justice", - "wellbeing" - ] - }, - { - "title": "Community Garden", - "summary": "A range of resources from videos to start- up guides for creating your own community garden in North Belfast.", - "link": "http://www.grow-ni.org/", - "tags": [ - "Sustainability", - "Volunteering", - "Social Economy", - "North Belfast" - ] - }, - { - "title": "Enterprise Advice", - "summary": "A service run by the North City Business Centre offers a range of business support services to assist entrepreneurs in starting a business.", - "link": "https://www.north-city.co.uk/business-advice/", - "tags": [ - "Economic", - "Skills", - "Support", - "North Belfast" - ] - }, - { - "title": "Personal and Family Development", - "summary": "Training and development opportunities provided by Ashton Community Trust including for “essential skills” and around employability.", - "link": "https://www.ashtoncentre.com/training-available/", - "tags": [ - "Economic", - "Skills", - "Training", - "North Belfast" - ] - }, - { - "title": "Forward South Partnership", - "summary": "A charity that works with community, schools, business, public and private sector partners to help regenerate and sustain a healthy and prosperous South Belfast.", - "link": "https://forwardsouth.org/", - "tags": [ - "Sustainability", - "Planning", - "Regeneration", - "South Belfast" - ] - }, - { - "title": "Employability South", - "summary": "A market development association designed to offer support to individuals from the local communities of South Belfast.", - "link": "https://mda.creativeworkerscooperative.com/issues/education-employment/employability-south/", - "tags": [ - "Economic", - "Training", - "Support", - "South Belfast" - ] - }, - { - "title": "Market Development Association", - "summary": "An association that seeks to advocate on behalf of the Market community on socio-economic issues which impact on the residents by adopting a community development approach.", - "link": "https://mda.creativeworkerscooperative.com/about-us/", - "tags": [ - "Regeneration", - "Economic", - "Planning", - "South Belfast" - ] - }, - { - "title": "East Belfast Enterprise", - "summary": "An incubation workspace hosting training/mentoring initiatives to support new and established businesses in the local area. The company works for the benefit of businesses in areas of Belfast and surrounding areas through a partnership approach with other organisations.", - "link": "https://www.eastbelfast.org/", - "tags": [ - "Enterprise", - "Economic", - "Skills", - "Support" - ] - }, - { - "title": "Community Advice Services", - "summary": "This resource links you to a service providing a free, independent, accessible and confidential advice, information and representation service using a community development approach.", - "link": "https://www.ebiac.org/services", - "tags": [ - "Service", - "Economic", - "Skills", - "Support" - ] - }, - { - "title": "Community Resource and Finance", - "summary": "This resource links you to a service promoting good financial management practice in the community sector by offering financial management advice, training services and a community accountancy service.", - "link": "https://www.ebcda.org/community-resource-finance", - "tags": [ - "Service", - "Economic", - "Skills", - "Support" - ] - }, - { - "title": "Colin Glen - Social Economy", - "summary": "This resource links you to a social economy trust that is dedicated to enhancing local communities by providing a range of modern facilities for developing individuals and improving their quality of life.", - "link": "https://www.colinglen.org/", - "tags": [ - "Enterprise", - "Economic", - "Regeneration", - "Planning" - ] - }, - { - "title": "Footprints Community Garden", - "summary": "This resource links you to a community garden that is an open place to grow organic food as well as opportunities for recreation, volunteering, exercise, therapy and education.", - "link": "https://www.footprintswomenscentre.org/communitygardens", - "tags": [ - "Sharedspace", - "Wellbeing", - "Sustainability", - "Support" - ] - }, - { - "title": "Building Your Future River", - "summary": "This resource links you to a community centre providing employment programmes in all sectors for adults not in work and/or economically inactive. ", - "link": "http://sallygardens.org/building-your-future-employability-project/", - "tags": [ - "Group", - "Economic", - "Youth", - "Skills", - "Planning" - ] - }, - { - "title": "Work West Enterprise Agency", - "summary": "This resource links you to an agency supporting businesses on their entrepreneurial journeys.", - "link": "https://workwest.co.uk/", - "tags": [ - "Enterprise", - "Economic", - "All", - "Regeneration", - "Planning" - ] - }, - { - "title": "Community Capacity Building", - "summary": "This resource links you to a community capacity- building programme including OCN courses, personal development, empowerment programmes, community relations, civic leadership and community resilience.", - "link": "https://www.facebook.com/colinneighbourhoodpartnership/posts/a-big-thank-you-to-digiskills-for-helping-us-learn-how-to-use-social-media-and-z/4590891624300878/", - "tags": [ - "Group", - "Economic", - "All", - "Skills", - "Support" - ] - } - ], - "Case Study": [ - { - "title": "Ashton FabLab", - "summary": "An innovative makerspace that includes a 3D printer, a laser cutter, CNC router, full electronics station, and a large scale vinyl cutter.", - "link": "https://www.ashtoncentre.com/services/design-fabrication/", - "image": "resource_images/North_Belfast_Case_Study_Ashton_FabLab.jpg", - "body": "A FabLab (Digital Fabrication Laboratory) is halfway between a laboratory and a workshop. It’s a place where you can make (almost) anything, where both small children and inventors can turn an idea into reality.", - "tags": [ - "Economic", - "Skills", - "Social Economy", - "North Belfast" - ] - }, - { - "title": "Peace Walls River", - "summary": "A group that assists communities impacted by the peace walls and physical barriers, providing a range of confidence and relationship building interventions within and between interface communities.", - "link": "https://ccrf.org.uk/peace-walls-project", - "image": "resource_images/North_Belfast_Case_Study_Peace_Walls_Project.png", - "body": "A partnership between Cliftonville Community Regeneration Forum (CCRF) and Lower Oldpark Community Association (LOCA). Our Peace Walls Programme focuses on three key areas of work: (1) Robust Community Consultations (2) Relationship building (3) Creating attitudinal change.", - "tags": [ - "Good Relations", - "Interface", - "Skills", - "North Belfast" - ] - }, - { - "title": "Art Learning Projects", - "summary": "A neutral venue for the presentation of contemporary visual art and associated creative and educational activities located on an interface in North Belfast.", - "link": "https://www.goldenthreadgallery.co.uk/project_category/learning/", - "image": "resource_images/North_Belfast_Case_Study_Art_Learning_Projects.png", - "body": "A neutral venue for creative and educational activities located on an interface in North Belfast providing an environment where the residents of Belfast regardless of political religious ethnic or sensual persuasion can experience contemporary visual art.", - "tags": [ - "Shared Space", - "Culture", - "Skills", - "North Belfast" - ] - }, - { - "title": "North Belfast Men's Shed", - "summary": "An enterprise where men make and sell their own products, communicate and socialise, and support each other when in need.", - "link": "https://menssheds.ie/sheds/north-belfast-community-mens-shed/", - "image": "resource_images/North_Belfast_Case_Study_North_Belfast_Men's_Shed.jpg", - "body": "A place where men can talk to each other about different things that are going on in their lives. If they have any kind of issues they support each other through it.", - "tags": [ - "Community", - "Health", - "Support", - "North Belfast" - ] - }, - { - "title": "Kitchen Crisis", - "summary": "A sustainable community food growing, donation, and collection river.", - "link": "https://thewarehousenewtownards.com/crisis-kitchen-1", - "image": "resource_images/North_Belfast_Case_Study_Kitchen_Crisis.png", - "body": "A sustainable community food river. Includes opportunities to Grow food in our community garden, Donate food from your fridge or cupboard, Collect food from local stores, Cook up a storm in the kitchen, Help us organise all the food as it comes in Deliver it to those in need.", - "tags": [ - "Service", - "Wellbeing", - "Social Economy", - "North Belfast" - ] - }, - { - "title": "Belfast South - Digital Hub", - "summary": "A range of equipment such as 3D printing, laser cutting, coding, photography, STEAM activities, sublimation with training, education and programmes.", - "link": "https://www.bscr.co.uk/digital-hub", - "image": "resource_images/South_Belfast_Case_Study_Belfast_South_Digital_Hub.png", - "body": "A digital hub that boasts a range of equipment that can help unleash your potential through: 3D printing, laser cutting, coding, photography, STEAM activities, sublimation and lots more.", - "tags": [ - "Shared space", - "Skills", - "Social Economy", - "South Belfast" - ] - }, - { - "title": "The Crescent Hub", - "summary": "A space that has been home to many arts organisations - DoubleBand Films, Fortnight Magazine, Belfast Community Circus, Open Arts, The Fenderesky Gallery and others.", - "link": "https://crescentarts.org/the-hub", - "image": "resource_images/South_Belfast_Case_Study_The_Crescent_Hub.png", - "body": "The Crescent has been home to many arts organisations - DoubleBand Films, Fortnight Magazine, Belfast Community Circus, Open Arts, The Fenderesky Gallery - to name but a few. We continue to be a home for creative organisations, many of them deeply connected into activity in the building. We are proud to be an Arts Centre that is both a home to, and platform, for so much talent and knowledge, and connected into multiple networks across Belfast, Northern Ireland, and beyond.", - "tags": [ - "Shared Space", - "Creativity", - "Skills", - "South Belfast" - ] - }, - { - "title": "Engaging for Change", - "summary": "A 26 week programme aimed at getting 8 ‘NEET’ young people (ages 14-20) back into education, training and employment.", - "link": "https://lorag.org/", - "image": "resource_images/South_Belfast_Case_Study_Engaging_for_Change.jpg", - "body": "A 26 week programme aimed at getting 8 ‘NEET’ young people (ages 14 -20) back into education, training and employment tailored to the individual needs of the young people.", - "tags": [ - "Group", - "Training", - "Young People", - "South Belfast" - ] - }, - { - "title": "Creating Cohesive Community", - "summary": "A service providing a three days’ nutritionally balanced emergency food and support to local people who are referred to us in crisis.", - "link": "https://lorag.org/community/", - "image": "resource_images/South_Belfast_Case_Study_Creating_Cohesive_Community.jpg", - "body": "River developed out of a Belfast City Council’s PEACE III Strategic Grants Programme to improve relations across communities.", - "tags": [ - "Community", - "Good Relations", - "Planning", - "South Belfast" - ] - }, - { - "title": "Refresh Café", - "summary": "This resource links you to a social enterprise helping a thousand vulnerable people every week to have a roof over their heads, get into work and improve their mental health", - "link": "https://www.ebm.org.uk/refresh-cafe/", - "image": "resource_images/East_Belfast_Case_Study_Refresh_Cafe.png", - "body": "Is a social enterprise, our profits enable East Belfast Mission to help a thousand vulnerable people every week to have a roof over their heads, get into work and improve their mental health. Every weekday we provide hot meals to over 250 local people who otherwise wouldn’t get a good nutritious meal and we provide training for scores of people who are unemployed, helping them get back into work. We deliver even more than great tasting food – we make food that does good. We pride ourselves on excellent customer service and a warm welcome for everyone. The cafe sources the finest local ingredients to create great tasting food.", - "tags": [ - "Enterprise", - "Economic", - "Regeneration", - "Support", - "Socialeconomy" - ] - }, - { - "title": "Restore Shops", - "summary": "This resource links you to a social economy project selling a range of good quality second hand clothes, furniture, books, bric-a-brac, refurbished furniture, bikes and other household items.", - "link": "https://www.ebm.org.uk/retail/", - "image": "resource_images/East_Belfast_Case_Study_Restore_Shops.jpg", - "body": "Restore is a social economy project of East Belfast Mission. Social Economy is also known as “not-for-profit” as income is used to sustain and further develop social and environmental aims within an organisation rather than being distributed to financial investors. Restore sells a range of good quality second hand clothes, furniture, books, bric-a-brac, refurbished furniture and bikes and other household items such as mirrors and pictures.", - "tags": [ - "Enterprise", - "Economic", - "ustainability", - "ESupport", - "Socialeconomy" - ] - }, - { - "title": "Hosforad", - "summary": "This resource links you to a service supporting people who are homeless or at risk of homelessness through a homeless hostel and a tenancy support service.", - "link": "ttps://www.ebm.org.uk/hosford/", - "image": "resource_images/East_Belfast_Case_Study_Hosforad.jpg", - "body": "Hosford is the homeless service of East Belfast Mission based in the Skainos Centre on the Lower Newtownards Road in East Belfast and for over 20 years Hosford has provided services to people who are homeless or at risk of homelessness. Hosford provides two main services a homeless hostel containing 26 rooms/apartments for people who have lost their home and a Tenancy Support Service. The tenancy support service is available for people who have their own home to help them maintain their independence and avoid homelessness.", - "tags": [ - "Sharedspace", - "Economic", - "Support", - "Skills" - ] - }, - { - "title": "The Foundry", - "summary": "This resource links you to a space fostering collaboration in a shared and supportive space, offering bench desking for up to 21 people and 10 small offices designed with a young and fresh startup in mind.", - "link": "https://www.eastbelfast.org/the-foundry/", - "image": "resource_images/East_Belfast_Case_Study_The_Foundry.jpg", - "body": "The Foundry is much more than just a desk and internet access, it’s an energising environment that fosters collaboration in a shared and supportive space. The Foundry is a co-working space offering bench desking for up to 21 people and 10 small offices designed with a young and fresh startup in mind who value privacy yet still want to be part of a collaborative vibe. We have a meeting room that can accommodate 6 people and a breakout area, communal kitchen, flat-screen TV, complimentary tea & coffee and current business magazines. We have also designed a studio/creative space for clients who need specific design space and unique desk layout to facilitate drawing and computer work. The mixture of open plan desks, small 2/3 man offices and studio/creative space offers our clients an alternative to just a boring desk in either a cramped uninspiring office or working from home.", - "tags": [ - "Goodrelations", - "Roomhire", - "Sharedspace", - "Economic" - ] - }, - { - "title": "Community Counselling", - "summary": "This resource links you to a service providing accessible, confidential counselling in East Belfast", - "link": "https://eastbelfastcounselling.org/", - "image": "resource_images/East_Belfast_Case_Study_Community_Counselling.jpeg", - "body": "Our aim is to provide accessible, confidential counselling in East Belfast. This includes services such as life coaching as well.", - "tags": [ - "Service", - "Wellbeing", - "Skills", - "Support" - ] - }, - { - "title": "Feel Good with Food", - "summary": "This resource links you to a cooking group that is free of charge to those living and working in the Urban Village area of East Belfast.", - "link": "https://www.communityni.org/news/feel-good-food", - "image": "resource_images/East_Belfast_Case_Study_Feel_Good_with Food.png", - "body": "", - "tags": [ - "Support", - "Skills", - "Wellbeing", - "Meeting" - ] - }, - { - "title": "Eastside Voices Tour", - "summary": "This resource links you to a tour involving authentic stories of conflict, division and pain that came to these streets from local guides who have been personally affected but yet remained resilient against adversity.", - "link": "http://www.eastsidevoices.com/", - "image": "resource_images/East_Belfast_Case_Study_Eastside_VoicesTour.jpg", - "body": "Hear authentic stories of conflict, division and pain that came to these streets from local guides who have been personally affected but yet remained resilient against adversity; as they guide you on a journey of discovery, understanding, and awareness of how communities who were once at war are now working together to promote community-led tourism and peacebuilding.", - "tags": [ - "Culture", - "Enterprise", - "Goodrelations", - "Tourism" - ] - }, - { - "title": "Community Counselling", - "summary": "This resource links you to a community based counselling service for adults, young people, and those with special needs.", - "link": "http://newlifecounselling.net/services/", - "image": "resource_images/West_Belfast_Case_Study_Community_Counselling.png", - "body": "Community based counselling for adults, young people, and those with special needs such as the deaf.", - "tags": [ - "Service", - "Wellbeing", - "Health", - "Support" - ] - }, - { - "title": "Colin Connect", - "summary": "This resource links you to the Colin Connect Transport Hub that includes two community managed spaces and a civic square that can be used to host a variety of public events and community activities.", - "link": "https://www.communityni.org/news/official-launch-colin-connect-transport-hub-and-colin-town-square", - "image": "resource_images/West_Belfast_Case_Study_Colin_Connect.jpg", - "body": "The new transport hub and civic square forms part of a major investment in the Colin area which is being supported through the Urban Villages Initiative. The Colin Connect Transport Hub has already improved the physical environment and offers new connections linking West Belfast, East Belfast and Titanic Quarter via the city centre on the Translink Glider service. The hub also includes two community spaces which will be managed by Colin Neighbourhood Partnership, who will also use the civic square to host a variety of public events and community activities.", - "tags": [ - "Sharedspace", - "Economic", - "Goodrelations", - "Roomhire" - ] - }, - { - "title": "Colin Summer Festival", - "summary": "This resource links you to a free festival organised by the Colin Neighbourhood Partnership that includes a Family fun day.", - "link": "https://www.communityni.org/news/colin-summer-festival-promises-bumper-programme-events-0", - "image": "resource_images/West_Belfast_Case_Study_Colin_Summer_Festival.jpg", - "body": "The festival, organised by Colin Neighbourhood Partnership and funded by the Urban Villages Initiative, will feature a range of events for all the family to get involved in. Highlights include the annual Colin Summer Festival Parade and Family Fun Day on Thursday 8th August. This annual carnival parade will make its way up from St Luke’s Church in Twinbrook, along the Stewartstown Road, to the new Colin Town square for a free Family Fun Day. Here there will be carnival rides, face painters, live music, arts and crafts, talent competitions, bouncy castles and much more. This free event is guaranteed to keep the kids entertained for hours!", - "tags": [ - "Meeting", - "Culture", - "Goodrelations", - "Volunteer" - ] - }, - { - "title": "Hillcrest Trust", - "summary": "This resource links you to an organisation regenerating areas of Derry.", - "link": "https://www.communityni.org/organisation/hillcrest-trust", - "image": "", - "body": "Hillcrest Trust is a community based organisation which works to support the physical and social regeneration of the Top of the Hill and Greater Waterside area of Derry City, through adoption of a community development approach.", - "tags": [ - "Derry", - "Regeneration", - "Shared spaces" - ] - }, - { - "title": "Destined Learning Disability Centre", - "summary": "This resource links you to a centre that seeks to address the needs of people with learning disabilities in a whole life context.", - "link": "http://www.destined.ie/", - "image": "resource_images/Derry_Case_Study_Destined.jpg", - "body": "Based in the upgraded Foyle Valley Railway Museum, it provides an essential resource for members with learning disabilities. This refurbishment meets DDA compliance to enable a whole family approach to membership and community inclusion.", - "tags": [ - "Derry", - "Education", - "Special needs" - ] - }, - { - "title": "Cathedral Youth Club", - "summary": "This resource links you to an empowering youth club.", - "link": "https://www.facebook.com/cathedralyouthclubfountain/", - "image": "", - "body": "Recently refurbishment of the existing Youth Club to meet the needs of a youth population in the Fountain area. The centre offers a bespoke approach to youth activities and family events.", - "tags": [ - "Derry", - "Youth", - "Skills", - "Wellbing" - ] - }, - { - "title": "Bishop Street Community Centre", - "summary": "This resource links you to an empowering youth club.", - "link": "https://bishopstreetyouthclub.com/", - "image": "resource_images/Derry_Case_Study_bishopstreetyouthclub-logo.jpg", - "body": "Based in the heart of Derry city, Bishop Street Youth Club celebrates and encourages young people to be the best they can be, by offering inclusive opportunities all year round.", - "tags": [ - "Derry", - "Youth", - "Skills", - "Wellbeing" - ] - }, - { - "title": "New Gates Arts and Cultural Centre", - "summary": "This resource links you to a vibrant community arts centre.", - "link": "https://www.newgatearts.com/", - "image": "resource_images/Derry_Case_Study_NewGatesArts.png", - "body": " A vibrant arts and culture centre, where everyone can experience the unique cultural heritage of our community that provides a range of workshops, classes, performances, talks, tours, festivals and cultural events. As well as facilitated discussions, good relations workshops and peace building initiatives.", - "tags": [ - "Derry", - "Arts", - "Shared space", - "Good relations" - ] - }, - { - "title": "Creggan County Park", - "summary": "This resource links you to a social economy project promoting recreation.", - "link": "https://creggancountrypark.com/", - "image": "resource_images/Derry_Case_Study_creggan-cp-logo.png", - "body": "Creggan Country Park is a non profit taking social economy enterprise designed to provide recreational, training and employment opportunities for the local community.", - "tags": [ - "Derry", - "Social economy", - "Training", - "Outdoors" - ] - }, - { - "title": "The Beam Centre", - "summary": "This resource links you to a co-working space for community projects.", - "link": "https://www.bitcni.org.uk/programmes/the-beam-centre/", - "image": "", - "body": "BEAM offers a coworking, conference & meeting space that supports community projects, start-ups and professionals by nurturing collaboration and development.", - "tags": [ - "Derry", - "Shared space", - "Community development" - ] - }, - { - "title": "In Your Space Circus", - "summary": "This resource links you to a group that uses street art for promoting good relations.", - "link": "https://www.inyourspaceni.org/", - "image": "resource_images/Derry_Case_Study_InYourSpaceCircus.png", - "body": "In Your Space aims to unleash the power of circus and street performance to ignite the imagination and inspire creativity and learning which can transform lives and communities.", - "tags": [ - "Derry", - "Culture", - "Good relations", - "Shared spaces" - ] - }, - { - "title": "Údarás na Gaeltachta", - "summary": "This resource links you to an Irish cultural and enterprise agency.", - "link": "https://udaras.ie/en/", - "image": "resource_images/Donegal_Case_Study_Udaras_Na_Gaeltachta.png", - "body": "A regional state agency whose primary objective is to preserve and strengthen Irish as a living language, as well as passing it on to the next generation. It works to achieve this by fostering enterprise development and employment and supporting community, cultural and language-based events.", - "tags": [ - "Donegal", - "Culture", - "Heritage", - "Enterprise" - ] - }, - { - "title": "Comhar na nOileán", - "summary": "This resource links you an organisation that supports island populations.", - "link": "https://oileain.ie", - "image": "", - "body": "An organisation that supports island populations and communities by promoting equal and equitable access and opportunity to education, training and employment services through the delivery of programmes and initiatives using area-based local development strategies and a bottom up, community driven development approach.", - "tags": [ - "Donegal", - "Island", - "Employment", - "Regeneration" - ] - }, - { - "title": "Muintir Na Tíre", - "summary": "This resource links you to an organisation dedicated to community development.", - "link": "https://www.muintir.ie", - "image": "resource_images/Donegal_Case_Study_muintir-logo-w-tagline.png", - "body": "A voluntary organisation dedicated to promoting the process of community development. It aims to enhance the capacities of people in communities, rural and urban, to become involved in local social, economic, cultural and environmental development.", - "tags": [ - "Donegal", - "Regeneration", - "Volunteers" - ] - }, - { - "title": "Rethink Ireland", - "summary": "This resource links you to an organisation that provides funding for community innovation.", - "link": "https://rethinkireland.ie", - "image": "", - "body": "An organisation that provides grants and business support to the social innovations who can make a real difference. Our task is to fuel these innovations with the knowledge and the advice they’ll need to succeed on a nationally impactful scale.", - "tags": [ - "Donegal", - "Innovation", - "Funding", - "Regeneration" - ] - }, - { - "title": "The Wheel", - "summary": "This resource links you to an organisation that advocates for communities and provides them training.", - "link": "https://www.wheel.ie", - "image": "resource_images/Donegal_Case_Study_The_Wheel.png", - "body": "An organisation that acts as a representative voice and helps to provide leadership to the charity and community sector and advocates on behalf of its community of members. As a supportive resource, it offers advice, training and other opportunities to people working or volunteering in the charity and community sector.", - "tags": [ - "Donegal", - "Voice", - "Training" - ] - }, - { - "title": "Innovating Communities", - "summary": "This resource links you to a project providing skills for community innovation.", - "link": "https://www.innovating.ie/", - "image": "resource_images/Donegal_Case_Study_Innovating_Communities.png", - "body": "A two year projecting being delivered across the border counties of Donegal, Leitrim, Sligo, Cavan, Monaghan and Louth, that empowers communities to tackle local challenges with Design Thinking and provides training and mentoring.", - "tags": [ - "Donegal", - "Innovation", - "Training" - ] - }, - { - "title": "Leader Funding", - "summary": "This resource links you to a programme that provides funding and training for community development.", - "link": "https://www.gov.ie/en/service/87e09-leader-programme-for-rural-development/.", - "image": "resource_images/Donegal_Case_Study_LEader_Funding.png", - "body": "A programme that provides funding and community development. It is administered at a local level by 29 local action groups, which contain local representatives from the community, public and private sector. Each group is responsible for selecting and awarding LEADER funding to projects within their geographical area.", - "tags": [ - "Donegal", - "Leadership", - "Training", - "Funding" - ] - }, - { - "title": "Pobal", - "summary": "This resource links you to an organisation supporting social inclusion.", - "link": "https://www.pobal.ie/", - "image": "resource_images/Donegal_Case_Study_PobalLogo.png", - "body": "An organisation that works on behalf of Government to support communities and local agencies toward achieving social inclusion and development.", - "tags": [ - "Donegal", - "Inclusion", - "Development" - ] - }, - { - "title": "PPN", - "summary": "This resources links you to a service that promotes community knowledge sharing and representation.", - "link": "https://www.donegalcoco.ie/community/supportingcommunities/donegalpublicparticipationnetworkppn/", - "image": "resource_images/Donegal_Case_Study_PPN.png", - "body": "Through the PPN organisations can cooperate, share information and ideas and elect members to Local Authority decision making bodies and relevant community structures.", - "tags": [ - "Donegal", - "Voice", - "Innovation" - ] - }, - { - "title": "Social Inclusion and Community Activation Network", - "summary": "SICAP aims to support communities and target groups to engage with relevant stakeholders in identifying and addressing...", - "link": "https://www.gov.ie/en/policy-information 6609f4-social-inclusion-and-community-activation-programme-sicap/", - "image": "resource_images/Donegal_Case_Study_Social_Inclusion_Community_Activation_Network.jpg", - "body": "SICAP aims to support communities and target groups to engage with relevant stakeholders in identifying and addressing social exclusion and equality issues, developing the capacity of local community groups and creating more sustainable communities.", - "tags": [ - "Donegal", - "Inclusion", - "Development" - ] - }, - { - "title": "Social Enterprise Plus", - "summary": "This resource links you to a Funding Programme supporting social enterprises to develop: (1) new and sustained job creation (2) new and improved services for Housing Executive Communities.", - "link": "https://www.nihe.gov.uk/Community/Community-Involvement/Social-Enterprise-Plus-Programme", - "image": "resource_images/Northern_Ireland_Case_Study_Social_Enterprise_Plus.jpg", - "body": "", - "tags": [ - "Enterprise", - "Skills", - "Planning" - ] - }, - { - "title": "Go for It - City Startups", - "summary": "This resource links you to an enterprise developing new solutions to real-world social, economic or environmental challenges.", - "link": "https://www.belfastcity.gov.uk/gosocial", - "image": "", - "body": "Developing new solutions to real-world social, economic or environmental challenges! Community Innovators a creative approach to problem-solving putting end-users at the centre of the solution focusing on experimentation, prototyping and evidence- gathering as key methods. The programme is free to access and is for voluntary, community or social enterprise organisations who would like support to explore and develop new solutions to a real-world social, economic or environmental challenge. All organisations who complete the Community Innovators Programme will be eligible to apply for a grant from the Foundation’s Social Innovation Seed Fund to help them deliver the idea or ideas they develop through participation in the programme.", - "tags": [ - "Planning", - "Enterprise", - "Skills" - ] - }, - { - "title": "Groundwork", - "summary": "This resource links you to a federation of charities mobilising practical community action on poverty and the environment across the UK.", - "link": "https://www.groundwork.org.uk/", - "image": "resource_images/Northern_Ireland_Case_Study_Groundwork.jpg", - "body": "Groundwork is a federation of charities mobilising practical community action on poverty and the environment across the UK. We’re passionate about creating a future where every neighbourhood is vibrant and green, every community is strong and able to shape its own destiny and no-one is held back by their background or circumstances.", - "tags": [ - "Economic", - "Sustainability", - "Planning", - "Socialeconomy", - "Group" - ] - }, - { - "title": "Belfast Interface Project", - "summary": "Belfast Interface Project is a membership organisation developing creative approaches to the regeneration of Belfast's interface or ‘peaceline’ areas.", - "link": "https://www.belfastinterfaceproject.org/", - "image": "resource_images/Northern_Ireland_Case_Study_BIPLogo.png", - "body": "This resource links you to Belfast Interface Project that is a membership organisation developing creative approaches to the regeneration of Belfast's interface or ‘peaceline’ areas.", - "tags": [ - "Group", - "Safety", - "Goodrelations", - "Planning", - "Interfaces" - ] - }, - { - "title": "Northern Ireland Alternatives", - "summary": "This resource links you to restorative justice programme that aims to promote and develop non-violent community responses to the issues of low-level crime and anti-social behaviour in areas across Northern Ireland.", - "link": "https://alternativesrj.co.uk/", - "image": "resource_images/Northern_Ireland_Case_Study_Northern_Ireland_Alternatives.png", - "body": "Northern Ireland Alternatives (NIA) is a government accredited restorative justice programme that aims to promote and develop non-violent community responses to the issues of low-level crime and anti- social behaviour in areas across Northern Ireland. We currently have branches in North Belfast, Greater Shankill, East Belfast, South Belfast and North Down.", - "tags": [ - "Group", - "Safety", - "Goodrelations", - "Support" - ] - }, - { - "title": "Belfast Healthy Cities", - "summary": "This resource links you to Belfast Healthy Cities working with stakeholders from government departments, universities, the private, public, voluntary and community.", - "link": "https://www.belfasthealthycities.com/", - "image": "resource_images/Northern_Ireland_Case_Study_Belfast_Healthy_Cities.png", - "body": "Belfast Healthy Cities, will work with stakeholders from government departments, universities, the private, public, voluntary and community.", - "tags": [ - "Group", - "Wellbeing", - "Health", - "Support" - ] - }, - { - "title": "Community Alley Growing", - "summary": "This resource links you to an innovative and sustainable local Community Alley Growing.", - "link": "https://www.farmgarden.org.uk/community-alley-growing", - "image": "resource_images/Northern_Ireland_Case_Study_Community_Alley_Growing.png", - "body": "The West Belfast Partnership Board opened the newly greened alleyway behind La Salle Gardens off the Falls Road. The West Belfast Partnership Board (WBPB) teamed up with Springvale Learning to transform the alleyway behind La Salle Gardens adjacent to Willowbank walkway, from a ‘dead space’ into a residential friendly, green area. WBPB was allocated funding from Northern Ireland Environment Link (NIEL) through the NGO Challenge Fund from the Carrier Bag levy, to undertake this alleyway regeneration projct.", - "tags": [ - "Sharedspace", - "Wellbeing", - "Sustainabilit", - "Support", - "Socialeconomy" - ] - }, - { - "title": "The 4 Corners Festival", - "summary": "This resource links you to the 4 Corners Festival that seeks to inspire people from across Belfast to transform it for the peace and wellbeing of all.", - "link": "https://4cornersfestival.com/", - "image": "resource_images/Northern_Ireland_Case_Study_The_4_Corners.png", - "body": "The 4 Corners Festival seeks to inspire people from across Belfast to transform it for the peace and wellbeing of all. It features innovative events designed to entice people out of their own ‘corners’ of the city and into new places where they will encounter new perspectives, new ideas and hopefully meet new friends. The Festival takes place annually in February. It comprises a range of events featuring discussion, music, prayer, drama, poetry and story- telling in venues across the city of Belfast.", - "tags": [ - "Meeting", - "Wellbeing", - "Goodrelations", - "Support" - ] - }, - { - "title": "Cara Friend", - "summary": "This resource links you to an organisation supporting lesbian, gay, bisexual and trans voluntary organisation in Northern Ireland.", - "link": "https://cara-friend.org.uk/", - "image": "resource_images/Northern_Ireland_Case_Study_Cara_Friend.png", - "body": "Cara-Friend is the oldest and largest lesbian, gay, bisexual and trans voluntary organisation in Northern Ireland. Cara- Friend is a grass roots community organisation which prides itself on diversity, progress, respect and tolerance. We work ceaselessly on behalf of Northern Ireland’s LGBT community and we welcome volunteers, so get in touch!", - "tags": [ - "Group", - "Wellbeing", - "Goodrelations", - "LGBTQ", - "Support" - ] - }, - { - "title": "Belfast Community Planning Partnership", - "summary": "This resource links you to the Belfast Community Planning Partnership with organisations named as community planning partners in legislation.", - "link": "https://www.belfastcity.gov.uk/community/community-planning/community-planning-for-belfast", - "image": "resource_images/Northern_Ireland_Case_Study_Belfast_Community_Planning_Partnership.png", - "body": "While developing the community plan, we formed the Belfast Community Planning Partnership, with organisations named as community planning partners in legislation. They have a specific duty to participate in the community planning process.", - "tags": [ - "Group", - "Economic", - "Regeneration", - "Planning" - ] - }, - { - "title": "Co-opeartive Aleternatives", - "summary": "This resources links you to a group offering high quality range of advice and tailored training on legal, financial, business and democratic governance.", - "link": "https://www.coopalternatives.coop/", - "image": "resource_images/Social_Economy_Case_Study_coop-alternatives-logo.png", - "body": "Co-operative Alternatives is the only body in Northern Ireland entirely devoted to developing successful co-operatives and community benefit societies. We offer high quality range of advice on legal, financial, business and democratic governance. We provide tailored training and business support to all groups who want to do business in a co-operative way.", - "tags": [ - "Belfast", - "Co-operatives", - "Enterprise", - "Training" - ] - }, - { - "title": "Utopia Project", - "summary": "This resources links you to a project offers Creative Learning and Social Activities to help reduce social isolation, improve the mental and physical health of older people in Andersonstown.", - "link": "https://www.communityni.org/organisation/utopia-project-upper-andersonstown-community-forum", - "image": "", - "body": "UTOPIA project funded by the Big Lottery, offers Creative Learning and Social Activities to help reduce social isolation, improve the mental and physical health and build the confidence and community involvement of older people in the Andersonstown area.", - "tags": [ - "Belfast", - "Technology", - "Training" - ] - }, - { - "title": "Connected Community Centre", - "summary": "This resources links you to an initiative connecting people to local health and wellbeing support services to help them keep well.", - "link": "https://www.communityni.org/organisation/connected-community-care", - "image": "resource_images/Social_Economy_Case_Study_Skainos.jpg", - "body": "The Connected Community Care service is an initiative developed by the Belfast Integrated Care Partnerships to connect people to local health and wellbeing support services to help them keep well. As a citywide social prescribing service, Connected Community.", - "tags": [ - "Belfast", - "Wellbeing", - "Health", - "Service" - ] - }, - { - "title": "Fablab Belfast", - "summary": "This resources links you to a makerspace in North Belfast.", - "link": "https://www.fablabni.com/centre/fablab-belfast.html", - "image": "resource_images/Social_Economy_Case_Study_FABLAB.png", - "body": "A makerspace in North Belfast encouraging community development, enterprise, and good relations.", - "tags": [ - "Belfast", - "Technology", - "Social Economy" - ] - }, - { - "title": "Farset Labs", - "summary": "This resources links you to a set of labs that are a place for creativity and technological tinkering and are open to all no matter what they want to do.", - "link": "http://farsetlabs.org.uk/", - "image": "resource_images/Social_Economy_Case_Study_FarsetLabs.jpg", - "body": "Farset Labs is a place for creativity and technological tinkering. We’re open to everyone, no matter what you want to do, and we welcome people from all walks of life to use our space, come to our events, and get involved with the maker community in Northern Ireland.", - "tags": [ - "Belfast", - "Technology", - "Skills", - "Social Economy" - ] - }, - { - "title": "Bannana Block", - "summary": "This resource links you to a shared community enterprise space.", - "link": "https://bananablock.org/", - "image": "resource_images/Social_Economy_Case_Study_Bananna_Block.png", - "body": "Banana Block is a new living museum and events space set within a historic linen mill and inspired by the curious connections between Belfast and bananas. Peel the banana a little further and you’ll find a space for original ideas, creative learning and new connections. A space for celebrating eccentric histories and creating incredible futures.", - "tags": [ - "Belfast", - "Social Economy", - "Good Relations", - "Enterprise", - "Shared Space" - ] - }, - { - "title": "Social Economy Village", - "summary": "This resources links you to an award winning organisation which supports businesses on their entrepreneurial journeys by delivering mentoring and training for aspiring and existing private and social entrepreneurs in West Belfast and beyond.", - "link": "http://www.workwest.co.uk/", - "image": "resource_images/Social_Economy_Case_Study_Work_West.png", - "body": "Social enterprises locating into the Social Economy Village will benefit in a range of ways: An opportunity to locate onto a purpose built site; Networking potential with like minded companies who serve a community as well as economic purpose; A supportive environment – through business development support and mentoring from Work West.", - "tags": [ - "Belfast", - "Social Economy", - "Enterprise" - ] - }, - { - "title": "Bobbins Café", - "summary": "This resources links you to a social economy café designed to support investment in tourism infrastructur.", - "link": "https://www.sustainableni.org/case-study/social-enterprise-caf%C3%A9-belfast-city-hall", - "image": "resource_images/Social_Economy_Case_Study_BobbinsCafe.jpg", - "body": "Development of a social economy café designed to support investment in tourism infrastructure.", - "tags": [ - "Belfast", - "Enterprise", - "Health", - "Social Economy" - ] - }, - { - "title": "Belfast Tool Library", - "summary": "This resource links you to a tool library in Belfast.", - "link": "https://www.belfasttoollibrary.com/", - "image": "resource_images/Social_Economy_Case_StudyBalfast_Tool_Lib.png", - "body": "Belfast Tool Library is the first tool lending library in Northern Ireland. Tool Libraries work just like any other library. You become a member and then you can borrow tools.", - "tags": [ - "Belfast", - "Tools", - "Social Economy" - ] - } - ] - }, "Organisations": [ { "name": "Refresh Café", @@ -1084,62 +104,6 @@ "link": "https://southbelfast.foodbank.org.uk/" } ], - "User Avatars": [ - { - "avatar": "avatars/angelfish.png" - }, - { - "avatar": "avatars/anglerfish.png" - }, - { - "avatar": "avatars/barb.png" - }, - { - "avatar": "avatars/blue_butterfly_fish.png" - }, - { - "avatar": "avatars/clownfish.png" - }, - { - "avatar": "avatars/comet_goldfish.png" - }, - { - "avatar": "avatars/discus.png" - }, - { - "avatar": "avatars/dolphin.png" - }, - { - "avatar": "avatars/goldfish.png" - }, - { - "avatar": "avatars/green_butterfly_fish.png" - }, - { - "avatar": "avatars/lionfish.png" - }, - { - "avatar": "avatars/masked_angelfish.png" - }, - { - "avatar": "avatars/mystery_fish.png" - }, - { - "avatar": "avatars/petitella.png" - }, - { - "avatar": "avatars/platy.png" - }, - { - "avatar": "avatars/red_butterfly_fish.png" - }, - { - "avatar": "avatars/undeclared_fish.png" - }, - { - "avatar": "avatars/wise_salmon.png" - } - ], "Users": [ { "display name": "Margaret Cloud", diff --git a/templates/messaging/chatbox_snippet.html b/templates/messaging/chatbox_snippet.html index 718837b3..dcbffc77 100644 --- a/templates/messaging/chatbox_snippet.html +++ b/templates/messaging/chatbox_snippet.html @@ -1,290 +1,295 @@ {% if user.is_anonymous %} - {{ user_anonymous_message }} + {{ user_anonymous_message }} {% elif user not in members %} - {{ not_member_message }} + {{ not_member_message }} {% else %} -
- {% csrf_token %} - - - - - -
-
- {% comment %} + + {% csrf_token %} + + + + + +
+
+ {% comment %} Note that this only provides a hint to the browser as to what file-types to display to the user, but this can be easily circumvented, so you should always validate the uploaded file on the server also. {% endcomment %} - - - -
- - {% include "partials/horizontal-spacer.html" with space="1.5" %} - -
- - - - - -
- - {% if river.current_stage == stage and request.user.id in starters and poll_possible and members|length > 2 %} - {% if poll_ref == None and not poll_ref.passed %} - - {% endif %} - {% endif %} + + + - - - {# where the modal goes - needs to be outside most of the divs to render properly #} -
+
+ + {% include "partials/horizontal-spacer.html" with space="1.5" %} + +
+ +
+ + +
+ +
+
+
+ + {% if river.current_stage == stage and request.user.id in starters and poll_possible and members|length > 2 %} + {% if poll_ref == None and not poll_ref.passed %} + + {% endif %} + {% endif %} +
+ + + {# where the modal goes - needs to be outside most of the divs to render properly #} +
{% endif %} {% block scripts %} - + } + + {% endblock %} From a61206ede276feacb22c5ccda624c481636b1bc7 Mon Sep 17 00:00:00 2001 From: nekton Date: Sun, 13 Oct 2024 23:40:13 +0100 Subject: [PATCH 5/7] ability to reopen the poll --- apps/river/views.py | 25 ++++++++++++------------ templates/messaging/chatbox_snippet.html | 3 +-- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/apps/river/views.py b/apps/river/views.py index 9795104b..c791a519 100644 --- a/apps/river/views.py +++ b/apps/river/views.py @@ -329,20 +329,19 @@ def get_context_data(self, **kwargs): or (chat_poll.closed and not chat_poll.passed), "stage_ref": stage_ref, # TODO: Write in len(members) > 2 rather than handling in template with members|length>2 - "poll_possible": True - if kwargs["stage"] == "envision" - else ( - False - if kwargs["stage"] == "reflect" - else (kwargs["topic"] != "general") + "poll_possible": ( + True if kwargs["stage"] == "envision" and (chat_poll is None or not chat_poll.passed) + else False if kwargs["stage"] == "reflect" + else + kwargs["topic"] != "general" or ( - kwargs["topic"] == "general" - and stage_ref.money_poll - and stage_ref.money_poll.passed - and stage_ref.place_poll - and stage_ref.place_poll.passed - and stage_ref.time_poll - and stage_ref.time_poll.passed + kwargs["topic"] == "general" + and stage_ref.money_poll + and stage_ref.money_poll.passed + and stage_ref.place_poll + and stage_ref.place_poll.passed + and stage_ref.time_poll + and stage_ref.time_poll.passed ) ), "starters": RiverMembership.objects.filter( diff --git a/templates/messaging/chatbox_snippet.html b/templates/messaging/chatbox_snippet.html index dcbffc77..084b1270 100644 --- a/templates/messaging/chatbox_snippet.html +++ b/templates/messaging/chatbox_snippet.html @@ -71,9 +71,8 @@
- {% if river.current_stage == stage and request.user.id in starters and poll_possible and members|length > 2 %} - {% if poll_ref == None and not poll_ref.passed %} + {% if poll_ref == None or not poll_ref.passed %}