Skip to content

Conversation

ChrisChV
Copy link
Contributor

@ChrisChV ChrisChV commented Sep 22, 2025

Description

  • Adds the task, python api, and rest api view for bulk migration.
  • Refactor the code to share code between single migration and bulk migration.
  • Which edX user roles will this change impact? "Course Author", "Developer".

Supporting information

Testing instructions

Deadline

ASAP, before the Ulmo cut.

Other information

N/A

@openedx-webhooks
Copy link

openedx-webhooks commented Sep 22, 2025

Thanks for the pull request, @ChrisChV!

This repository is currently maintained by @openedx/wg-maintenance-edx-platform.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.


Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Sep 22, 2025
@ChrisChV ChrisChV marked this pull request as draft September 22, 2025 21:56
@github-project-automation github-project-automation bot moved this to Needs Triage in Contributions Sep 22, 2025
@mphilbrick211 mphilbrick211 added the FC Relates to an Axim Funded Contribution project label Sep 23, 2025
@mphilbrick211 mphilbrick211 moved this from Needs Triage to Waiting on Author in Contributions Sep 23, 2025
@ChrisChV ChrisChV force-pushed the chris/FAL-4253-bulk-library-migration branch from 6e1903d to c39836e Compare September 24, 2025 21:46
Copy link
Contributor

@rpenido rpenido left a comment

Choose a reason for hiding this comment

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

LGTM 👍
Thank you for your work, @ChrisChV!

collection_key=modified_key,
title=title,
)
except libraries_api.LibraryCollectionAlreadyExists as e:
Copy link
Contributor

Choose a reason for hiding this comment

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

Note: We are always creating new collections here. Therefore, if you migrate the same library multiple times, while the components are not duplicated (due to the default skip strategy), the collections are.
I'm not sure if there is an easy and pretty solution for this, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @rpenido, I created this 7503ce6 with a partial solution (See the comment). Review it and tell me how you see it, before creating tests for that.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it is good enough for now! It worked after some tweaks (see the review).
Thanks @ChrisChV.

@shared_task(base=_MigrationTask, bind=True)
# Note: The decorator @set_code_owner_attribute cannot be used here because the UserTaskMixin
# does stack inspection and can't handle additional decorators.
def migrate_from_modulestore(
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for breaking this function into smaller ones! ❤️

Copy link
Contributor

@rpenido rpenido left a comment

Choose a reason for hiding this comment

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

This worked after some tweaks.

Copy link
Contributor

@bradenmacdonald bradenmacdonald left a comment

Choose a reason for hiding this comment

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

Looks good. I had a bunch of questions and suggestions but they're all minor / optional.

@bradenmacdonald
Copy link
Contributor

@kdmccormick did you also want to review this?

Copy link
Contributor

@bradenmacdonald bradenmacdonald left a comment

Choose a reason for hiding this comment

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

Thanks for adding those very nice docstrings explaining the new functions / tasks, @ChrisChV! 💯

I think this is good to go now. @rpenido could you please just test it manually one more time to make sure the refactors didn't introduce any regressions? And then it's good to merge.

@mphilbrick211 mphilbrick211 moved this from Waiting on Author to In Eng Review in Contributions Oct 8, 2025
@rpenido
Copy link
Contributor

rpenido commented Oct 9, 2025

Sorry for the delay!
@ChrisChV I'm having the following error while selecting multiple legacy libraries for migration:

cms-1              | 2025-10-09 09:05:09,473 INFO 107 [tracking] [user 4] [ip 172.18.0.1] logger.py:41 - {"name": "/api/modulestore_migrator/v1/bulk_migration/", "context": {"user_id": 4, "path": "/api/modulestore_migrator/v1/bulk_migration/", "course_id": "", "org_id": "", "enterprise_uuid": ""}, "username": "edx", "session": "165a54beb4a6b7096b49cc2a96d2691b", "ip": "172.18.0.1", "agent": "Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0", "host": "studio.local.openedx.io:8001", "referer": "http://apps.local.openedx.io:2001/", "accept_language": "pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3", "event": "{\"GET\": {}, \"POST\": {}}", "time": "2025-10-09T09:05:09.473833+00:00", "event_type": "/api/modulestore_migrator/v1/bulk_migration/", "event_source": "server", "page": null}
cms-1              | 2025-10-09 09:05:09,520 ERROR 107 [celery.utils.dispatch.signal] [user 4] [ip 172.18.0.1] signal.py:284 - Signal handler <function task_failed at 0x7f8cba640c20> raised: TransactionManagementError("An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.")
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
cms-1              |     return self.cursor.execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
cms-1              |     return self.cursor.execute(query, args)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
cms-1              |     res = self._query(mogrified_query)
cms-1              |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
cms-1              |     db.query(q)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 280, in query
cms-1              |     _mysql.connection.query(self, query)
cms-1              | MySQLdb.IntegrityError: (1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              |
cms-1              | The above exception was the direct cause of the following exception:
cms-1              |
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/celery/app/trace.py", line 453, in trace_task
cms-1              |     R = retval = fun(*args, **kwargs)
cms-1              |                  ^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/edx-platform/cms/djangoapps/modulestore_migrator/tasks.py", line 585, in bulk_migrate_from_modulestore
cms-1              |     _migration = ModulestoreMigration.objects.create(
cms-1              |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 658, in create
cms-1              |     obj.save(force_insert=True, using=self.db)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save
cms-1              |     self.save_base(
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 877, in save_base
cms-1              |     updated = self._save_table(
cms-1              |               ^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 1020, in _save_table
cms-1              |     results = self._do_insert(
cms-1              |               ^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 1061, in _do_insert
cms-1              |     return manager._insert(
cms-1              |            ^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1805, in _insert
cms-1              |     return query.get_compiler(using=using).execute_sql(returning_fields)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1822, in execute_sql
cms-1              |     cursor.execute(sql, params)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 235, in execute
cms-1              |     return self._record(super().execute, sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 160, in _record
cms-1              |     return method(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 102, in execute
cms-1              |     return super().execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
cms-1              |     return self._execute_with_wrappers(
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
cms-1              |     return executor(sql, params, many, context)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
cms-1              |     with self.db.wrap_database_errors:
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
cms-1              |     raise dj_exc_value.with_traceback(traceback) from exc_value
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
cms-1              |     return self.cursor.execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
cms-1              |     return self.cursor.execute(query, args)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
cms-1              |     res = self._query(mogrified_query)
cms-1              |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
cms-1              |     db.query(q)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 280, in query
cms-1              |     _mysql.connection.query(self, query)
cms-1              | django.db.utils.IntegrityError: (1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              |
cms-1              | The above exception was the direct cause of the following exception:
cms-1              |
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/celery/utils/dispatch/signal.py", line 280, in send
cms-1              |     response = receiver(signal=self, sender=sender, **named)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/user_tasks/signals.py", line 237, in task_failed
cms-1              |     sender.status.fail(str(exception))
cms-1              |     ^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/user_tasks/tasks.py", line 78, in status
cms-1              |     return UserTaskStatus.objects.get(task_id=task_id)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 633, in get
cms-1              |     num = len(clone)
cms-1              |           ^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 380, in __len__
cms-1              |     self._fetch_all()
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1881, in _fetch_all
cms-1              |     self._result_cache = list(self._iterable_class(self))
cms-1              |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 91, in __iter__
cms-1              |     results = compiler.execute_sql(
cms-1              |               ^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1562, in execute_sql
cms-1              |     cursor.execute(sql, params)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 235, in execute
cms-1              |     return self._record(super().execute, sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 160, in _record
cms-1              |     return method(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 102, in execute
cms-1              |     return super().execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
cms-1              |     return self._execute_with_wrappers(
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
cms-1              |     return executor(sql, params, many, context)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 83, in _execute
cms-1              |     self.db.validate_no_broken_transaction()
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 530, in validate_no_broken_transaction
cms-1              |     raise TransactionManagementError(
cms-1              | django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.
cms-1              | 2025-10-09 09:05:09,521 ERROR 107 [celery.app.trace] [user 4] [ip 172.18.0.1] trace.py:267 - Task cms.djangoapps.modulestore_migrator.tasks.bulk_migrate_from_modulestore[e2fcb0c2-c97c-441f-8e3e-4b097734aa04] raised unexpected: IntegrityError(1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
cms-1              |     return self.cursor.execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
cms-1              |     return self.cursor.execute(query, args)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
cms-1              |     res = self._query(mogrified_query)
cms-1              |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
cms-1              |     db.query(q)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 280, in query
cms-1              |     _mysql.connection.query(self, query)
cms-1              | MySQLdb.IntegrityError: (1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              |
cms-1              | The above exception was the direct cause of the following exception:
cms-1              |
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/celery/app/trace.py", line 453, in trace_task
cms-1              |     R = retval = fun(*args, **kwargs)
cms-1              |                  ^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/edx-platform/cms/djangoapps/modulestore_migrator/tasks.py", line 585, in bulk_migrate_from_modulestore
cms-1              |     _migration = ModulestoreMigration.objects.create(
cms-1              |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 658, in create
cms-1              |     obj.save(force_insert=True, using=self.db)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save
cms-1              |     self.save_base(
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 877, in save_base
cms-1              |     updated = self._save_table(
cms-1              |               ^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 1020, in _save_table
cms-1              |     results = self._do_insert(
cms-1              |               ^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 1061, in _do_insert
cms-1              |     return manager._insert(
cms-1              |            ^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1805, in _insert
cms-1              |     return query.get_compiler(using=using).execute_sql(returning_fields)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1822, in execute_sql
cms-1              |     cursor.execute(sql, params)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 235, in execute
cms-1              |     return self._record(super().execute, sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 160, in _record
cms-1              |     return method(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 102, in execute
cms-1              |     return super().execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
cms-1              |     return self._execute_with_wrappers(
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
cms-1              |     return executor(sql, params, many, context)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
cms-1              |     with self.db.wrap_database_errors:
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
cms-1              |     raise dj_exc_value.with_traceback(traceback) from exc_value
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
cms-1              |     return self.cursor.execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
cms-1              |     return self.cursor.execute(query, args)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
cms-1              |     res = self._query(mogrified_query)
cms-1              |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
cms-1              |     db.query(q)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 280, in query
cms-1              |     _mysql.connection.query(self, query)
cms-1              | django.db.utils.IntegrityError: (1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              | 2025-10-09 09:05:09,525 ERROR 107 [root] [user None] [ip None] signals.py:22 - Uncaught exception from None
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
cms-1              |     return self.cursor.execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
cms-1              |     return self.cursor.execute(query, args)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
cms-1              |     res = self._query(mogrified_query)
cms-1              |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
cms-1              |     db.query(q)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 280, in query
cms-1              |     _mysql.connection.query(self, query)
cms-1              | MySQLdb.IntegrityError: (1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              |
cms-1              | The above exception was the direct cause of the following exception:
cms-1              |
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/celery/app/trace.py", line 453, in trace_task
cms-1              |     R = retval = fun(*args, **kwargs)
cms-1              |                  ^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/edx-platform/cms/djangoapps/modulestore_migrator/tasks.py", line 585, in bulk_migrate_from_modulestore
cms-1              |     _migration = ModulestoreMigration.objects.create(
cms-1              |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 658, in create
cms-1              |     obj.save(force_insert=True, using=self.db)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save
cms-1              |     self.save_base(
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 877, in save_base
cms-1              |     updated = self._save_table(
cms-1              |               ^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 1020, in _save_table
cms-1              |     results = self._do_insert(
cms-1              |               ^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 1061, in _do_insert
cms-1              |     return manager._insert(
cms-1              |            ^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1805, in _insert
cms-1              |     return query.get_compiler(using=using).execute_sql(returning_fields)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1822, in execute_sql
cms-1              |     cursor.execute(sql, params)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 235, in execute
cms-1              |     return self._record(super().execute, sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 160, in _record
cms-1              |     return method(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 102, in execute
cms-1              |     return super().execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
cms-1              |     return self._execute_with_wrappers(
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
cms-1              |     return executor(sql, params, many, context)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
cms-1              |     with self.db.wrap_database_errors:
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
cms-1              |     raise dj_exc_value.with_traceback(traceback) from exc_value
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
cms-1              |     return self.cursor.execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
cms-1              |     return self.cursor.execute(query, args)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
cms-1              |     res = self._query(mogrified_query)
cms-1              |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
cms-1              |     db.query(q)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 280, in query
cms-1              |     _mysql.connection.query(self, query)
cms-1              | django.db.utils.IntegrityError: (1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              |
cms-1              | The above exception was the direct cause of the following exception:
cms-1              |
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
cms-1              |     response = get_response(request)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
cms-1              |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/opt/pyenv/versions/3.11.8/lib/python3.11/contextlib.py", line 81, in inner
cms-1              |     return func(*args, **kwds)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
cms-1              |     return view_func(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/viewsets.py", line 125, in view
cms-1              |     return self.dispatch(request, *args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 515, in dispatch
cms-1              |     response = self.handle_exception(exc)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 475, in handle_exception
cms-1              |     self.raise_uncaught_exception(exc)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 486, in raise_uncaught_exception
cms-1              |     raise exc
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 512, in dispatch
cms-1              |     response = handler(request, *args, **kwargs)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/edx-platform/cms/djangoapps/modulestore_migrator/rest_api/v1/views.py", line 242, in create
cms-1              |     task_status = UserTaskStatus.objects.get(task_id=task.id)
cms-1              |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 633, in get
cms-1              |     num = len(clone)
cms-1              |           ^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 380, in __len__
cms-1              |     self._fetch_all()
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1881, in _fetch_all
cms-1              |     self._result_cache = list(self._iterable_class(self))
cms-1              |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 91, in __iter__
cms-1              |     results = compiler.execute_sql(
cms-1              |               ^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1562, in execute_sql
cms-1              |     cursor.execute(sql, params)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 235, in execute
cms-1              |     return self._record(super().execute, sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 160, in _record
cms-1              |     return method(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 102, in execute
cms-1              |     return super().execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
cms-1              |     return self._execute_with_wrappers(
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
cms-1              |     return executor(sql, params, many, context)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 83, in _execute
cms-1              |     self.db.validate_no_broken_transaction()
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 530, in validate_no_broken_transaction
cms-1              |     raise TransactionManagementError(
cms-1              | django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.
cms-1              | Internal Server Error: /api/modulestore_migrator/v1/bulk_migration/
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
cms-1              |     return self.cursor.execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
cms-1              |     return self.cursor.execute(query, args)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
cms-1              |     res = self._query(mogrified_query)
cms-1              |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
cms-1              |     db.query(q)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 280, in query
cms-1              |     _mysql.connection.query(self, query)
cms-1              | MySQLdb.IntegrityError: (1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              |
cms-1              | The above exception was the direct cause of the following exception:
cms-1              |
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/celery/app/trace.py", line 453, in trace_task
cms-1              |     R = retval = fun(*args, **kwargs)
cms-1              |                  ^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/edx-platform/cms/djangoapps/modulestore_migrator/tasks.py", line 585, in bulk_migrate_from_modulestore
cms-1              |     _migration = ModulestoreMigration.objects.create(
cms-1              |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 658, in create
cms-1              |     obj.save(force_insert=True, using=self.db)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save
cms-1              |     self.save_base(
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 877, in save_base
cms-1              |     updated = self._save_table(
cms-1              |               ^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 1020, in _save_table
cms-1              |     results = self._do_insert(
cms-1              |               ^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 1061, in _do_insert
cms-1              |     return manager._insert(
cms-1              |            ^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1805, in _insert
cms-1              |     return query.get_compiler(using=using).execute_sql(returning_fields)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1822, in execute_sql
cms-1              |     cursor.execute(sql, params)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 235, in execute
cms-1              |     return self._record(super().execute, sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 160, in _record
cms-1              |     return method(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 102, in execute
cms-1              |     return super().execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
cms-1              |     return self._execute_with_wrappers(
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
cms-1              |     return executor(sql, params, many, context)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
cms-1              |     with self.db.wrap_database_errors:
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
cms-1              |     raise dj_exc_value.with_traceback(traceback) from exc_value
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
cms-1              |     return self.cursor.execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
cms-1              |     return self.cursor.execute(query, args)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
cms-1              |     res = self._query(mogrified_query)
cms-1              |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
cms-1              |     db.query(q)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 280, in query
cms-1              |     _mysql.connection.query(self, query)
cms-1              | django.db.utils.IntegrityError: (1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              |
cms-1              | The above exception was the direct cause of the following exception:
cms-1              |
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
cms-1              |     response = get_response(request)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
cms-1              |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/opt/pyenv/versions/3.11.8/lib/python3.11/contextlib.py", line 81, in inner
cms-1              |     return func(*args, **kwds)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
cms-1              |     return view_func(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/viewsets.py", line 125, in view
cms-1              |     return self.dispatch(request, *args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 515, in dispatch
cms-1              |     response = self.handle_exception(exc)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 475, in handle_exception
cms-1              |     self.raise_uncaught_exception(exc)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 486, in raise_uncaught_exception
cms-1              |     raise exc
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 512, in dispatch
cms-1              |     response = handler(request, *args, **kwargs)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/edx-platform/cms/djangoapps/modulestore_migrator/rest_api/v1/views.py", line 242, in create
cms-1              |     task_status = UserTaskStatus.objects.get(task_id=task.id)
cms-1              |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 633, in get
cms-1              |     num = len(clone)
cms-1              |           ^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 380, in __len__
cms-1              |     self._fetch_all()
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1881, in _fetch_all
cms-1              |     self._result_cache = list(self._iterable_class(self))
cms-1              |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 91, in __iter__
cms-1              |     results = compiler.execute_sql(
cms-1              |               ^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1562, in execute_sql
cms-1              |     cursor.execute(sql, params)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 235, in execute
cms-1              |     return self._record(super().execute, sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 160, in _record
cms-1              |     return method(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 102, in execute
cms-1              |     return super().execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
cms-1              |     return self._execute_with_wrappers(
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
cms-1              |     return executor(sql, params, many, context)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 83, in _execute
cms-1              |     self.db.validate_no_broken_transaction()
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 530, in validate_no_broken_transaction
cms-1              |     raise TransactionManagementError(
cms-1              | django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.
cms-1              | 2025-10-09 09:05:09,612 ERROR 107 [django.request] [user None] [ip None] log.py:246 - Internal Server Error: /api/modulestore_migrator/v1/bulk_migration/
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
cms-1              |     return self.cursor.execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
cms-1              |     return self.cursor.execute(query, args)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
cms-1              |     res = self._query(mogrified_query)
cms-1              |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
cms-1              |     db.query(q)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 280, in query
cms-1              |     _mysql.connection.query(self, query)
cms-1              | MySQLdb.IntegrityError: (1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              |
cms-1              | The above exception was the direct cause of the following exception:
cms-1              |
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/celery/app/trace.py", line 453, in trace_task
cms-1              |     R = retval = fun(*args, **kwargs)
cms-1              |                  ^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/edx-platform/cms/djangoapps/modulestore_migrator/tasks.py", line 585, in bulk_migrate_from_modulestore
cms-1              |     _migration = ModulestoreMigration.objects.create(
cms-1              |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 658, in create
cms-1              |     obj.save(force_insert=True, using=self.db)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save
cms-1              |     self.save_base(
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 877, in save_base
cms-1              |     updated = self._save_table(
cms-1              |               ^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 1020, in _save_table
cms-1              |     results = self._do_insert(
cms-1              |               ^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 1061, in _do_insert
cms-1              |     return manager._insert(
cms-1              |            ^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1805, in _insert
cms-1              |     return query.get_compiler(using=using).execute_sql(returning_fields)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1822, in execute_sql
cms-1              |     cursor.execute(sql, params)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 235, in execute
cms-1              |     return self._record(super().execute, sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 160, in _record
cms-1              |     return method(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 102, in execute
cms-1              |     return super().execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
cms-1              |     return self._execute_with_wrappers(
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
cms-1              |     return executor(sql, params, many, context)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
cms-1              |     with self.db.wrap_database_errors:
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
cms-1              |     raise dj_exc_value.with_traceback(traceback) from exc_value
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
cms-1              |     return self.cursor.execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
cms-1              |     return self.cursor.execute(query, args)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
cms-1              |     res = self._query(mogrified_query)
cms-1              |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
cms-1              |     db.query(q)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 280, in query
cms-1              |     _mysql.connection.query(self, query)
cms-1              | django.db.utils.IntegrityError: (1062, "Duplicate entry '66' for key 'modulestore_migrator_modulestoremigration.task_status_id'")
cms-1              |
cms-1              | The above exception was the direct cause of the following exception:
cms-1              |
cms-1              | Traceback (most recent call last):
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
cms-1              |     response = get_response(request)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
cms-1              |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/opt/pyenv/versions/3.11.8/lib/python3.11/contextlib.py", line 81, in inner
cms-1              |     return func(*args, **kwds)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
cms-1              |     return view_func(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/viewsets.py", line 125, in view
cms-1              |     return self.dispatch(request, *args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 515, in dispatch
cms-1              |     response = self.handle_exception(exc)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 475, in handle_exception
cms-1              |     self.raise_uncaught_exception(exc)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 486, in raise_uncaught_exception
cms-1              |     raise exc
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 512, in dispatch
cms-1              |     response = handler(request, *args, **kwargs)
cms-1              |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/edx-platform/cms/djangoapps/modulestore_migrator/rest_api/v1/views.py", line 242, in create
cms-1              |     task_status = UserTaskStatus.objects.get(task_id=task.id)
cms-1              |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
cms-1              |     return getattr(self.get_queryset(), name)(*args, **kwargs)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 633, in get
cms-1              |     num = len(clone)
cms-1              |           ^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 380, in __len__
cms-1              |     self._fetch_all()
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1881, in _fetch_all
cms-1              |     self._result_cache = list(self._iterable_class(self))
cms-1              |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 91, in __iter__
cms-1              |     results = compiler.execute_sql(
cms-1              |               ^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1562, in execute_sql
cms-1              |     cursor.execute(sql, params)
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 235, in execute
cms-1              |     return self._record(super().execute, sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/debug_toolbar/panels/sql/tracking.py", line 160, in _record
cms-1              |     return method(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 102, in execute
cms-1              |     return super().execute(sql, params)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
cms-1              |     return self._execute_with_wrappers(
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
cms-1              |     return executor(sql, params, many, context)
cms-1              |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 83, in _execute
cms-1              |     self.db.validate_no_broken_transaction()
cms-1              |   File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 530, in validate_no_broken_transaction
cms-1              |     raise TransactionManagementError(
cms-1              | django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.
cms-1              | [09/Oct/2025 09:05:09] "POST /api/modulestore_migrator/v1/bulk_migration/ HTTP/1.1" 500 452054

Please let me know if you need any additional information to reproduce it.

@ChrisChV
Copy link
Contributor Author

ChrisChV commented Oct 9, 2025

I'm having the following error while selecting multiple legacy libraries for migration:

@rpenido, I merged this branch with master. It works for me. Maybe you need to run make migrate in the cms bash. What I see is that it is missing change task_status to ForeignKey to erase the unique constraint.

@rpenido
Copy link
Contributor

rpenido commented Oct 10, 2025

You're right @ChrisChV! It was missing the migration. Sorry for the noise!

@bradenmacdonald I manually tested it and it is working as expected.

@bradenmacdonald
Copy link
Contributor

OK, this is good to go then.

@ChrisChV
Copy link
Contributor Author

ChrisChV commented Oct 11, 2025

@rpenido Thanks! Could you make another kick review of b38cbdf. There are new small requirements: openedx/frontend-app-authoring#2201 (comment)

@ChrisChV ChrisChV requested a review from rpenido October 13, 2025 15:22
Copy link
Contributor

@rpenido rpenido left a comment

Choose a reason for hiding this comment

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

LGTM 👍
Thank you for your work @ChrisChV!

  • I tested this using the instructions from the PR
  • I read through the code
  • I checked for accessibility issues
  • Includes documentation

This is working as expected!
I left you a comment about the datetime format.

from django.utils.translation import gettext_lazy as _

from common.djangoapps.split_modulestore_django.models import SplitModulestoreCourseIndex
from common.djangoapps.util.date_utils import strftime_localized, DEFAULT_LONG_DATE_FORMAT
Copy link
Contributor

Choose a reason for hiding this comment

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

DEFAULT_LONG_DATE_FORMAT doesn't include time. Should we use DEFAULT_DATE_TIME_FORMAT instead?

Suggested change
from common.djangoapps.util.date_utils import strftime_localized, DEFAULT_LONG_DATE_FORMAT
from common.djangoapps.util.date_utils import strftime_localized, DEFAULT_DATE_TIME_FORMAT

key = slugify(title)
collection = None
attempt = 0
created_at = strftime_localized(datetime.now(timezone.utc), DEFAULT_LONG_DATE_FORMAT)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
created_at = strftime_localized(datetime.now(timezone.utc), DEFAULT_LONG_DATE_FORMAT)
created_at = strftime_localized(datetime.now(timezone.utc), DEFAULT_DATE_TIME_FORMAT)

@ChrisChV ChrisChV enabled auto-merge (squash) October 13, 2025 21:27
@ChrisChV ChrisChV merged commit 3db4399 into openedx:master Oct 13, 2025
48 checks passed
@ChrisChV ChrisChV deleted the chris/FAL-4253-bulk-library-migration branch October 13, 2025 21:34
@github-project-automation github-project-automation bot moved this from In Eng Review to Done in Contributions Oct 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

FC Relates to an Axim Funded Contribution project open-source-contribution PR author is not from Axim or 2U

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants