Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(documentation): partie 8, associer les Topic avec les Document #793

Draft
wants to merge 40 commits into
base: 765-part7
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
02e00f6
remove /documentation/ routes to reused it with documentation app
vincentporte Sep 23, 2024
7fca7f0
deactivate weekly matomo forums stats until collect becomes compatibl…
vincentporte Sep 23, 2024
ae2e44f
add AbstractPublication and DatedModel in utils
vincentporte Sep 30, 2024
ca12af2
reuse abstract DatedModel in models
vincentporte Sep 30, 2024
bacdf13
reuse abstract Publication in partner app, renamed logo into image
vincentporte Sep 30, 2024
8d30b80
setup documentation app
vincentporte Sep 23, 2024
7439254
add models, admin, factories, admin and tests
vincentporte Sep 30, 2024
915b985
add datas migration for Category and Document
vincentporte Sep 24, 2024
833f10a
update taggeditems of Forum migrated into Document
vincentporte Sep 24, 2024
0491e84
add CategoryListView
vincentporte Sep 24, 2024
fff82ec
reinstall link to documentation route
vincentporte Sep 24, 2024
ea38765
add CategoryDetailView plus links to it in CategoryListView
vincentporte Sep 24, 2024
c628c00
setup get_absolute_url on Category model
vincentporte Sep 24, 2024
d2259ac
apply wrap_iframe_in_div_tag in abstracted Publication model
vincentporte Sep 24, 2024
04d09a3
add CategoryCreateView
vincentporte Sep 24, 2024
b9f42a3
show link to documentation:category_create for superuser only in docu…
vincentporte Sep 24, 2024
5e59ed5
add CategoryUpdateView
vincentporte Sep 24, 2024
e241b22
show link to documentation:category_update for superuser only in docu…
vincentporte Sep 24, 2024
a2e007e
update breadcrumb
vincentporte Sep 25, 2024
e5c3de7
add get_absolute_url in Document model
vincentporte Sep 25, 2024
8879592
add missing test for parse_response_to_soup
vincentporte Sep 25, 2024
de8010c
replace current date in a soup for snapshot, given a specified format
vincentporte Sep 25, 2024
60497b6
add DocumentDetailView
vincentporte Sep 25, 2024
6ecbc48
link DocumentDetailView in CategoryDetailView
vincentporte Sep 25, 2024
59bd9bd
simplify content_summary template
vincentporte Sep 30, 2024
e646afb
make CategoryDetailView able to manage HX Request
vincentporte Sep 30, 2024
b5720e8
add filtering on tag in CategoryDetailView
vincentporte Sep 30, 2024
9b6874b
add a test on queries count
vincentporte Sep 30, 2024
1286377
thanks to tests, fix content_summary template
vincentporte Oct 1, 2024
482a55b
add DocumentRating model
vincentporte Oct 1, 2024
02735bd
add ForumRating migration into DocumentRating
vincentporte Oct 1, 2024
38fe27a
add ForumRatingView
vincentporte Oct 1, 2024
194f009
show ForumRating in DocumentDetailView, if rating exists for the docu…
vincentporte Oct 1, 2024
08efdae
update ForumStatWeekArchiveView to display DocumentRating instead of …
vincentporte Oct 1, 2024
cfed057
remove ForumRatingView
vincentporte Oct 1, 2024
5e1fdbd
remove ForumRatingFactory
vincentporte Oct 1, 2024
d2aa52d
remove unusefull property on Forum model, related to ForumRating
vincentporte Oct 1, 2024
6da8f9e
add Document FK on Topics
vincentporte Oct 2, 2024
53018fd
associate Topics with Document when migrating Forum
vincentporte Oct 2, 2024
a8aa882
associate Topics with Document when migrating Forum
vincentporte Oct 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion clevercloud/collect_weekly_matomo_forum_stats.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ fi
# $APP_HOME is set by default by clever cloud.
cd $APP_HOME

python manage.py collect_matomo_forum_stats
# reactivate after documentation migration
# python manage.py collect_matomo_forum_stats
echo "Collecting weekly matomo stats for the forum is disabled for now."
1 change: 1 addition & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"lacommunaute.search",
"lacommunaute.surveys",
"lacommunaute.partner",
"lacommunaute.documentation",
]

INSTALLED_APPS = DJANGO_APPS + LOCAL_APPS + THIRD_PARTIES_APPS
Expand Down
2 changes: 2 additions & 0 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.urls import include, path, re_path
from machina.core.loading import get_class

from lacommunaute.documentation import urls as documentation_urls
from lacommunaute.event import urls as event_urls
from lacommunaute.forum import urls as forum_extension_urls
from lacommunaute.forum_conversation import urls as forum_conversation_extension_urls
Expand All @@ -30,6 +31,7 @@
path("inclusion_connect/", include(inclusion_connect_urls)),
# www.
path("", include(pages_urls)),
path("documentation/", include(documentation_urls)),
path("members/", include(forum_member_urls)),
path("", include(forum_conversation_extension_urls)),
path("", include(forum_extension_urls)),
Expand Down
Empty file.
40 changes: 40 additions & 0 deletions lacommunaute/documentation/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from django.contrib import admin

from lacommunaute.documentation.models import Category, Document, DocumentRating


class DocumentInlines(admin.TabularInline):
model = Document
extra = 0
fields = ("name", "short_description")
readonly_fields = ("name", "short_description")

def has_delete_permission(self, request, obj=None):
return False

def has_add_permission(self, request, obj=None):
return False


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ("name",)
search_fields = ("name",)
fields = ("name", "short_description", "description", "image")
inlines = [DocumentInlines]


@admin.register(Document)
class DocumentAdmin(admin.ModelAdmin):
list_display = ("name", "category")
list_filter = ("category", "partner")
search_fields = ("name",)
fields = ("name", "short_description", "description", "image", "category", "partner", "certified", "tags")


@admin.register(DocumentRating)
class DocumentRatingAdmin(admin.ModelAdmin):
list_display = ("document", "rating", "created")
list_filter = ("document",)
list_display_links = ("rating",)
raw_id_fields = ("document", "user")
8 changes: 8 additions & 0 deletions lacommunaute/documentation/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.apps import AppConfig


class ForumUpVoteAppConfig(AppConfig):
label = "documentation"
name = "lacommunaute.documentation"
verbose_name = "Documentation"
verbose_name_plural = "Documentation"
63 changes: 63 additions & 0 deletions lacommunaute/documentation/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import factory
from faker import Faker

from lacommunaute.documentation.models import Category, Document, DocumentRating


faker = Faker()


class AbstractDocumentationFactory(factory.django.DjangoModelFactory):
name = factory.Faker("name")
description = factory.Faker("sentence", nb_words=100)
short_description = factory.Faker("sentence", nb_words=10)
image = factory.django.ImageField(filename="banner.jpg")


class CategoryFactory(AbstractDocumentationFactory):
class Meta:
model = Category

class Params:
for_snapshot = factory.Trait(
name="Test Category", description="Test description", short_description="Test short description"
)


class DocumentFactory(AbstractDocumentationFactory):
category = factory.SubFactory(CategoryFactory)

class Meta:
model = Document

class Params:
for_snapshot = factory.Trait(
name="Test Document",
description="Test description",
short_description="Test short description",
category=factory.SubFactory(CategoryFactory, for_snapshot=True),
)

@factory.post_generation
def with_tags(self, create, extracted, **kwargs):
if not create or not extracted:
return

if isinstance(extracted, list):
for tag in extracted:
self.tags.add(tag)


class DocumentRatingFactory(factory.django.DjangoModelFactory):
document = factory.SubFactory(DocumentFactory)
rating = factory.Faker("random_int", min=1, max=5)

class Meta:
model = DocumentRating
skip_postgeneration_save = True

@factory.post_generation
def set_created(self, create, extracted, **kwargs):
if extracted:
self.created = extracted
self.save()
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import sys

from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand
from taggit.models import TaggedItem

from lacommunaute.documentation.models import Category, Document, DocumentRating
from lacommunaute.forum.models import Forum, ForumRating
from lacommunaute.forum_conversation.models import Topic


def create_categories_from_catforums():
# TODO next :
# add redirections when get_absolute_url is setup
transpo_dict = {}

for forum in Forum.objects.filter(type=1, level=0):
category = Category.objects.create(
name=forum.name,
short_description=forum.short_description,
description=forum.description,
image=forum.image,
)
transpo_dict[forum] = category

return transpo_dict


def create_document_from_forums(category_transpo_dict):
# TODO next :
# add redirections when get_absolute_url is setup
# migrate UpVotes

forum_content_type = ContentType.objects.get_for_model(Forum)
document_content_type = ContentType.objects.get_for_model(Document)
transpo_dict = {}

for forum in Forum.objects.filter(parent__type=1):
document = Document.objects.create(
name=forum.name,
short_description=forum.short_description,
description=forum.description,
image=forum.image,
category=category_transpo_dict[forum.parent],
partner=forum.partner,
certified=forum.certified,
)
TaggedItem.objects.filter(content_type=forum_content_type, object_id=forum.id).update(
content_type=document_content_type, object_id=document.id
)
transpo_dict[forum] = document

return transpo_dict


def migrate_ratings(document_transpo_dict):
document_ratings = [
DocumentRating(
document=document_transpo_dict[rating.forum],
session_id=rating.session_id,
rating=rating.rating,
user=rating.user,
created=rating.created,
updated=rating.updated,
)
for rating in ForumRating.objects.all()
]
DocumentRating.objects.bulk_create(document_ratings)
ForumRating.objects.all().delete()


def migrate_topics(document_transpo_dict):
main_forum = Forum.objects.get_main_forum()

for forum, document in document_transpo_dict.items():
topics = Topic.objects.filter(forum=forum)
sys.stdout.write(f"*** {len(topics)} topics to migrate from {forum} ({forum.id}) to {main_forum}\n")

for topic in topics:
topic.document = document
topic.forum = main_forum
topic.save()
forum.save()


def del_forums(category_transpo_dict, document_transpo_dict):
forums_to_delete = list(category_transpo_dict.keys()) + list(document_transpo_dict.keys())
return Forum.objects.filter(pk__in=[forum.pk for forum in forums_to_delete]).delete()


class Command(BaseCommand):
help = "migration des forums de fiches pratiques vers la documentation"

def handle(self, *args, **options):
sys.stdout.write("let's go!\n")

category_transpo_dict = create_categories_from_catforums()
sys.stdout.write("Categories created\n")

document_transpo_dict = create_document_from_forums(category_transpo_dict)
sys.stdout.write("Documents created\n")

migrate_ratings(document_transpo_dict)
sys.stdout.write("Ratings migrated\n")

migrate_topics(document_transpo_dict)
sys.stdout.write("Topics migrated\n")

## TODO next : Stats

deleted_forums = del_forums(category_transpo_dict, document_transpo_dict)
sys.stdout.write(f"{deleted_forums} forums deleted\n")

sys.stdout.write("that's all folks!")
sys.stdout.flush()
114 changes: 114 additions & 0 deletions lacommunaute/documentation/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Generated by Django 5.0.9 on 2024-09-30 14:14

import django.db.models.deletion
import machina.models.fields
import storages.backends.s3
import taggit.managers
from django.db import migrations, models

import lacommunaute.utils.validators


class Migration(migrations.Migration):
initial = True

dependencies = [
("partner", "0003_remove_partner_logo_partner_image_alter_partner_name_and_more"),
("taggit", "0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx"),
]

operations = [
migrations.CreateModel(
name="Category",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("created", models.DateTimeField(auto_now_add=True, verbose_name="Creation date")),
("updated", models.DateTimeField(auto_now=True, verbose_name="Update date")),
("name", models.CharField(max_length=100, unique=True, verbose_name="Name")),
("slug", models.SlugField(max_length=255, verbose_name="Slug")),
(
"description",
machina.models.fields.MarkupTextField(
blank=True, no_rendered_field=True, null=True, verbose_name="Description"
),
),
("short_description", models.CharField(max_length=400, verbose_name="Short Description")),
(
"image",
models.ImageField(
blank=True,
null=True,
storage=storages.backends.s3.S3Storage(bucket_name="private-bucket", file_overwrite=False),
upload_to="",
validators=[lacommunaute.utils.validators.validate_image_size],
),
),
("_description_rendered", models.TextField(blank=True, editable=False, null=True)),
],
options={
"verbose_name": "Catégorie",
"verbose_name_plural": "Catégories",
"ordering": ["created"],
},
),
migrations.CreateModel(
name="Document",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("created", models.DateTimeField(auto_now_add=True, verbose_name="Creation date")),
("updated", models.DateTimeField(auto_now=True, verbose_name="Update date")),
("name", models.CharField(max_length=100, unique=True, verbose_name="Name")),
("slug", models.SlugField(max_length=255, verbose_name="Slug")),
(
"description",
machina.models.fields.MarkupTextField(
blank=True, no_rendered_field=True, null=True, verbose_name="Description"
),
),
("short_description", models.CharField(max_length=400, verbose_name="Short Description")),
(
"image",
models.ImageField(
blank=True,
null=True,
storage=storages.backends.s3.S3Storage(bucket_name="private-bucket", file_overwrite=False),
upload_to="",
validators=[lacommunaute.utils.validators.validate_image_size],
),
),
(
"certified",
models.BooleanField(default=False, verbose_name="Certifié par la communauté de l'inclusion"),
),
("_description_rendered", models.TextField(blank=True, editable=False, null=True)),
(
"category",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="documents",
to="documentation.category",
),
),
(
"partner",
models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="partner.partner"
),
),
(
"tags",
taggit.managers.TaggableManager(
help_text="A comma-separated list of tags.",
through="taggit.TaggedItem",
to="taggit.Tag",
verbose_name="Tags",
),
),
],
options={
"verbose_name": "Document",
"verbose_name_plural": "Documents",
"ordering": ["-created"],
},
),
]
Loading
Loading