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

feat: CMD-187 autocreate search page #885

Draft
wants to merge 37 commits into
base: main
Choose a base branch
from

Conversation

wesleyboar
Copy link
Member

@wesleyboar wesleyboar commented Oct 22, 2024

Overview

Support a Google Search page with less effort.

What Effort?

Every Time:

  1. Create search engine.
  2. Configure search engine to match others.
  3. Set owners of search engine.
  4. Copy search codeID.
  5. Set relevant SEARCH settings.

Previously:

  1. Restart the server.
  2. Create /search page.
  3. Create snippet on page.
  4. Paste search code in snippet.
  5. Duplicate-or-Neglect search styles from TACC

Now, Instead:

  1. Restart the server.

Caution

Do not merge.

Warning

Successful run requires two deploys. Help wanted.

Related

Instances:

Changes

  • added search_page app
  • added SEARCH_AUTO_SETUP setting
  • added GOOGLE_SEARCH_ENGINE_ID setting

Testing

  1. Register a Google Programmable Search Engine.
  2. Add real GOOGLE_SEARCH_ENGINE_ID setting e.g.
    GOOGLE_SEARCH_ENGINE_ID = 'b099996c09ebd4ece'
  3. Add SEARCH_AUTO_SETUP = True setting.
  4. Run/Deploy website that has no search page.
  5. Verify "Search (Auto-Generated)" page exists.
  6. Search using search bar.
  7. ✅ Verify search page shows with results.
  8. Remove GOOGLE_SEARCH_ENGINE_ID setting.
  9. Run/Deploy site.
  10. Revisit search page.
  11. ✅ Verify warning appears on page.

UI

with GOOGLE_SEARCH_ENGINE_ID sans GOOGLE_SEARCH_ENGINE_ID
search is set search not set

So that it can be loaded from CDN. Use cases are:
- if search template is broken
- for custom search page
- for non-Core website
Compared to "cms-search", "google-search" is:
- more accurate
- more obvious to those not familiar with Core-CMS
So dev sees 'google-search' from CSS before seeing it in HTML `id` attr.
@wesleyboar wesleyboar marked this pull request as ready for review October 22, 2024 23:37
@wesleyboar wesleyboar changed the title feat: template & setting for Google search feat: search_page app Oct 24, 2024
Copy link
Member Author

@wesleyboar wesleyboar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts on why create_page() is run twice?

I have checks for existing page and fallbacks for missing page.

def ready(self):
if settings.SEARCH_PAGE_AUTO_SETUP:
from .utils import create_page
create_page()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

This create_page function is run twice on website startup.

1. Initial Deploy on Existing Site (When App Has Not Yet Been Installed).

Internal Server Error. Re-deploy required.

Log

Note: treebeard is an application that manages pages.

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/psycopg/cursor.py", line 732, in execute
    raise ex.with_traceback(None)
psycopg.errors.UniqueViolation: duplicate key value violates unique constraint "cms_treenode_path_key"
DETAIL:  Key (path)=(0004) already exists.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/code/taccsite_cms/wsgi.py", line 16, in <module>
    application = get_wsgi_application()
                  ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/core/wsgi.py", line 12, in get_wsgi_application
    django.setup(set_prefix=False)
  File "/usr/local/lib/python3.11/site-packages/django/__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/usr/local/lib/python3.11/site-packages/django/apps/registry.py", line 124, in populate
    app_config.ready()
  File "/code/apps/search_page/apps.py", line 10, in ready
    create_page()
  File "/code/apps/search_page/utils.py", line 46, in create_page
    page = create_cms_page(
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/cms/api.py", line 183, in create_page
    page.set_tree_node(site=site, target=target_node, position=position)
  File "/usr/local/lib/python3.11/site-packages/cms/models/pagemodel.py", line 474, in set_tree_node
    self.node = TreeNode.add_root(instance=new_node)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/treebeard/mp_tree.py", line 626, in add_root
    return MP_AddRootHandler(cls, **kwargs).process()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/treebeard/mp_tree.py", line 344, in process
    newobj.save()
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save
    self.save_base(
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 877, in save_base
    updated = self._save_table(
              ^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 1020, in _save_table
    results = self._do_insert(
              ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 1061, in _do_insert
    return manager._insert(
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 1805, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1822, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
    return executor(sql, params, many, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
    with self.db.wrap_database_errors:
  File "/usr/local/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/psycopg/cursor.py", line 732, in execute
    raise ex.with_traceback(None)
django.db.utils.IntegrityError: duplicate key value violates unique constraint "cms_treenode_path_key"
DETAIL:  Key (path)=(0004) already exists.
unable to load app 0 (mountpoint='') (callable not found or import error)
*** no app loaded. going in full dynamic mode ***
uWSGI running as root, you can use --uid/--gid/--chroot options
*** WARNING: you are running uWSGI as root !!! (use the --uid flag) *** 
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 1)
spawned uWSGI worker 1 (pid: 16, cores: 20)
spawned uWSGI worker 2 (pid: 24, cores: 20)
spawned uWSGI worker 3 (pid: 37, cores: 20)
spawned uWSGI worker 4 (pid: 66, cores: 20)
--- no python application found, check your startup logs for errors ---
[pid: 66|app: -1|req: -1/1] 129.114.111.125 () {68 vars in 2061 bytes} [Fri Oct 25 10:01:21 2024] GET /admin/cms/page/?language=en => generated 21 bytes in 0 msecs (HTTP/2.0 500) 2 headers in 83 bytes (0 switches on core 2)
--- no python application found, check your startup logs for errors ---
[pid: 16|app: -1|req: -1/2] 129.114.111.125 () {66 vars in 1951 bytes} [Fri Oct 25 10:01:26 2024] GET / => generated 21 bytes in 0 msecs (HTTP/2.0 500) 2 headers in 83 bytes (0 switches on core 1)

2. Re-Deploy.

Page is created and works. Logs note existing page.

Log
[DJANGO] INFO 2024-10-25 10:03:53,120 UTC utils portal.apps.search_page.utils.create_page:62: Found existing search page at "search"

3. Delete Search Page. Deploy Again.

Page is created and works. Logs note existing page.

Log
[DJANGO] INFO 2024-10-25 10:03:53,120 UTC utils portal.apps.search_page.utils.create_page:62: Found existing search page at "search"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

@wesleyboar wesleyboar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jarosenb gave me a good internal review:

It sounds like what you're trying to create is a custom migration, if this thing really needs to be a CMS page.

@taoteg suggested a guide:

A good migrations primer

A.I. has given me starter code.


I have the page creation function I can adapt. I believe I can replace the complexity of the app with one file.

I'll help you create a Django CMS Page using a database migration. This is useful for automatically creating pages during deployment.

Here's how to create a new migration file that will create a CMS page:

XXXX_create_custom_page.py
from django.db import migrations
from cms.api import create_page
from cms.models import Page

def create_cms_page(apps, schema_editor):
    # Skip if page already exists
    if not Page.objects.filter(title_set__title='Your Page Title').exists():
        page = create_page(
            title='Your Page Title',
            template='page.html',  # Use your template
            language='en',
            slug='your-page-slug',
            published=True,
            in_navigation=True,
        )
        page.publish('en')

def remove_cms_page(apps, schema_editor):
    # Delete the page if it exists
    Page.objects.filter(title_set__title='Your Page Title').delete()

class Migration(migrations.Migration):
    dependencies = [
        ('cms', '0022_auto_20180620_1551'),  # Adjust this to your last migration
    ]

    operations = [
        migrations.RunPython(create_cms_page, remove_cms_page)
    ]

To use this:

  1. Create a new migration file in your CMS app's migrations folder
  2. Replace XXXX in the filename with the next available migration number
  3. Adjust the dependencies to point to your last migration
  4. Customize the page properties in create_cms_page:
    • title: The page title
    • template: Your template name
    • slug: URL-friendly version of the title
    • in_navigation: Whether to show in menus

Then run:

python manage.py migrate

The migration will create the page if it doesn't exist already. The remove_cms_page function handles rollback if you need to reverse the migration.

Note: Make sure you have the necessary imports and that your template exists before running the migration.

@wesleyboar wesleyboar marked this pull request as draft October 31, 2024 19:24
@wesleyboar wesleyboar changed the title feat: search_page app feat: CMD-187 autocreate search page Oct 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant