Skip to content

Latest commit

 

History

History
321 lines (232 loc) · 13 KB

File metadata and controls

321 lines (232 loc) · 13 KB

Phase 0: Scaffolding — COMPLETION

Agent: Phase 0 Scaffolding
Branch: modernize/django5
Date: 2026-06-13


What Was Created

New project structure

pythonfiddle_modern/       ← new Django 5.2 project package
  __init__.py
  settings.py              ← fully configured (see below)
  urls.py                  ← minimal (admin only; URLs agent adds the rest)
  wsgi.py
  asgi.py
manage.py                  ← replaced with Django 5.2 version
requirements.txt           ← updated with modern deps
.gitignore                 ← added
docs/modernization/
  PORTED_MODELS.md         ← full model field documentation
  DATA_MIGRATION.md        ← placeholder + export/import pseudocode
  VALIDATION_CHECKLIST.md  ← placeholder for validation agent
  old_inspectdb.txt        ← "No existing DB found" (no legacy DB in repo)
COMPLETION.md              ← this file

Files left intact (legacy reference, READ ONLY)

__init__.py           ← legacy root package marker
context_processors.py ← legacy (not wired into new settings)
files.py              ← legacy static file lists (mediasync)
settings.default.py   ← legacy Django 1.4 settings (reference only)
urls.py               ← legacy root urlconf (reference only)
locale/               ← existing translation files (reusable)
templates/            ← existing templates (starting point for URLs agent)

Bootstrap Commands

# Activate the virtual environment
source /Users/yuguang/Projects/pythonfiddle-modernize/.venv/bin/activate

# Install cloud_ide from source (editable)
pip install -e /Users/yuguang/Projects/django-cloud-ide

# Run migrations
python manage.py migrate

# Start development server
python manage.py runserver 8001

The DJANGO_SETTINGS_MODULE is pre-configured in manage.py as pythonfiddle_modern.settings.


cloud_ide Package Install Status

  • Installed: Yes, editable install from /Users/yuguang/Projects/django-cloud-ide
  • Version: 1.2.0
  • Python version: 3.14.3
  • Django version: 5.2.15

Python 2 → Python 3 / Django 1.4 → Django 5.2 patches applied to cloud_ide

All patches were applied directly to the source at /Users/yuguang/Projects/django-cloud-ide:

File Change
cloud_ide/fiddle/compression.py Rewrote for Python 3: cStringIO→io, added local compress_string, fixed exception syntax, fixed db_type() to use connection.settings_dict
cloud_ide/fiddle/models.py Absolute imports; ugettext_lazy→gettext_lazy; @permalink→reverse(); added on_delete to both ForeignKeys; __unicode__→__str__
cloud_ide/fiddle/forms.py Fixed implicit relative import from models import *
cloud_ide/fiddle/admin.py Fixed implicit relative import
cloud_ide/fiddle/views.py render_to_response+RequestContext→render; simplejson→json; is_authenticated()→.is_authenticated; is_ajax()→headers check; mimetype→content_type; absolute imports
cloud_ide/fiddle/jsonresponse.py Rewrote to extend Django's built-in JsonResponse
cloud_ide/fiddle/templatetags/jqtmpl.py TOKEN_BLOCK/TOKEN_VAR→TokenType.BLOCK/VAR; TextNode from django.template.base

Note: cloud_ide/fiddle/migrations/0001_initial.py was auto-generated by makemigrations and lives in the cloud_ide source tree.


Key Models Found in django-cloud-ide

See docs/modernization/PORTED_MODELS.md for full details.

Language (fiddle_language)

  • id PK, name CharField(30)

Snippet (fiddle_snippet)

  • id, title(80), slug(100), author FK→User, description(300)
  • tags TaggableManager, last_modified auto_now, code CompressedTextField, language FK→Language

CompressedTextField: custom TextField that gzip-compresses code blobs in the DB.


Installed Apps (settings.py)

INSTALLED_APPS = [
    'whitenoise.runserver_nostatic',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sites',
    'django.contrib.sitemaps',
    'social_django',
    'taggit',
    'cloud_ide.fiddle',
]

Exit Criteria — PASSED

  • python manage.py migrate exits 0 (all migrations applied including fiddle.0001_initial)
  • python manage.py runserver 8001 starts without ImportError on Django 5.2.15


Phase 3: Static Assets — COMPLETE

Agent: Phase 3 Static Assets
Date: 2026-06-13

Changes Made

File Change
pythonfiddle_modern/settings.py Added STATICFILES_DIRS = [BASE_DIR / 'static']
static/favicon.ico Created minimal 16×16 transparent ICO placeholder

What Was Already in Place (from Phase 0)

  • STATIC_ROOT = BASE_DIR / 'staticfiles'
  • Whitenoise CompressedManifestStaticFilesStorage in STORAGES
  • WhiteNoiseMiddleware in MIDDLEWARE (after SecurityMiddleware) ✓
  • whitenoise.runserver_nostatic in INSTALLED_APPS
  • staticfiles/ in .gitignore
  • No MEDIASYNC or AWS_* settings present ✓

Verification Results

python manage.py collectstatic --noinput
→ 128 static files copied to staticfiles/, 384 post-processed

python manage.py check
→ System check identified no issues (0 silenced)

Exit Criteria — PASSED

  • collectstatic completes without errors
  • manage.py check reports 0 issues
  • staticfiles/ excluded from version control via .gitignore
  • No legacy mediasync/AWS settings in codebase
  • Committed and pushed to modernize/django5

Open Questions / Handoff Notes

  1. cloud_ide.login app is not in INSTALLED_APPS. It still imports from the old social_auth package. The Auth agent (Phase 2) must either patch it to use social_django or replace it entirely.
  2. cloud_ide.shared and cloud_ide.snippet are not in INSTALLED_APPS. The URLs agent (Phase 5) should evaluate whether these are needed.
  3. CLOUD_IDE_TEMPLATES_DIR is hardcoded to /Users/yuguang/Projects/cloud-ide-templates. Move to an env variable before production deployment.
  4. No legacy DB was found at the legacy project path. If a production DB dump is available, the Data agent should run inspectdb against it and update old_inspectdb.txt.
  5. django-chunks was removed. If any templates reference {% load chunks %}, those must be updated.
  6. Social auth credentials are blank in settings.py. The Auth agent must wire up the OAuth keys (via env vars).
  7. Snippet.slug has no unique=True constraint — the Models agent should evaluate adding it.

Phase 1: Models + Data

Agent: Phase 1 Models + Data Branch: modernize/django5 (commit a77f3e8) Date: 2026-06-14

What Was Done

Scripts created

File Purpose
scripts/export_legacy_data.py Reads legacy SQLite DB (no Django needed), dumps Language/Snippet/taggit to JSON, decompresses gzip blobs
scripts/import_legacy_data.py Reads JSON export, get_or_create Language, create Snippet with author fallback, re-applies tags
scripts/README.md Full usage docs + roundtrip example
scripts/__init__.py Package marker

Documentation updated

  • docs/modernization/DATA_MIGRATION.md — filled in with actual commands, column-level schema mapping, roundtrip test results, and open issues.

Bug fix: CompressedTextField.get_db_prep_save (Python 3)

The get_db_prep_save method was calling models.TextField.get_db_prep_save, which in Python 3 applies str() to the compressed bytes — storing b'\x1f\x8b...' as TEXT instead of a binary BLOB. Fixed by returning the compressed bytes directly, bypassing TextField's string conversion. Committed to django-cloud-ide @ eb5843d and pushed to origin/master.

Roundtrip Test — PASSED

Export:  Languages: 1  Snippets: 1
Import:  Snippets created: 1  Snippets skipped: 0
Verify:  code='print("hello")'  tags=['python','test']  author='testuser'  ✓

Open Questions / Handoff Notes

  1. No legacy production DB was available to test against real data. The roundtrip was validated on a freshly-seeded test DB.
  2. last_modified not preservedauto_now=True resets timestamps on import. Use raw SQL UPDATE if original timestamps matter.
  3. Snippet.slug uniqueness — still no unique=True. Evaluate before launch to prevent URL collisions.
  4. Placeholder user (legacy_import_user) is auto-created for orphaned snippets. Reassign via Django admin after user migration.
  5. auth_user / social auth not migrated — users must re-authenticate after launch.

Phase 2: Auth — COMPLETE

Agent: Phase 2 Auth
Branch: modernize/django5
Date: 2026-06-13

What Was Done

Settings (pythonfiddle_modern/settings.py)

Change Detail
import os Added at top of file
SOCIAL_AUTH_URL_NAMESPACE = 'social' Must match namespace= in urls.py
OAuth keys via os.environ.get() GOOGLE_KEY/SECRET, TWITTER_KEY/SECRET, FACEBOOK_KEY/SECRET

Settings already in place from Phase 0 (no changes needed):

  • social_django in INSTALLED_APPS
  • AUTHENTICATION_BACKENDS with Google/Twitter/Facebook + ModelBackend ✓
  • social_django.context_processors.backends and .login_redirect
  • LOGIN_URL = '/login/', LOGIN_REDIRECT_URL = '/', LOGOUT_REDIRECT_URL = '/'

URLs (pythonfiddle_modern/urls.py)

  • path('social-auth/', include('social_django.urls', namespace='social')) — all OAuth endpoints
  • path('login/', TemplateView.as_view(template_name='login.html'), name='login') — login page placeholder (Phase 5 will supply the real template)
  • path('logout/', LogoutView.as_view(next_page='/'), name='logout') — POST-only logout (Django 5 CSRF-safe)

Note: Mounted social_django.urls only once. Mounting it twice under the same namespace causes ImproperlyConfigured in Django 5.

cloud_ide.login status — SUPERSEDED (do not add to INSTALLED_APPS)

/Users/yuguang/Projects/django-cloud-ide/cloud_ide/login/ is legacy Django 1.4 code:

  • models.py imports from social_auth.signals and social_auth.backends.facebook (package removed)
  • urls.py uses from django.conf.urls.defaults import patterns (removed in Django 2)
  • views.py uses render_to_response + RequestContext (removed in Django 5) and is_authenticated() as a method call (fixed upstream in Phase 0)

Decision: Do not add cloud_ide.login to INSTALLED_APPS. social_django provides all necessary OAuth login/callback/disconnect views. The /login/ page (Phase 5) will render provider links using {% url 'social:begin' 'google-oauth2' %} etc.

If the CustomUser model from cloud_ide/login/models.py is needed, port it as a separate accounts app that uses AbstractUser — but for now the standard auth.User is sufficient.

Verification

python manage.py check
→ System check identified no issues (0 silenced)

python manage.py migrate
→ No migrations to apply  (social_django migrations were already applied in Phase 0)

reverse('social:begin', kwargs={'backend': 'google-oauth2'})  → /social-auth/login/google-oauth2/
reverse('social:complete', kwargs={'backend': 'google-oauth2'})  → /social-auth/complete/google-oauth2/
reverse('social:begin', kwargs={'backend': 'twitter'})  → /social-auth/login/twitter/
reverse('login')  → /login/
reverse('logout')  → /logout/

Exit Criteria — PASSED

  • social_django in INSTALLED_APPS, migrations applied
  • AUTHENTICATION_BACKENDS lists Google OAuth2, Twitter OAuth, Facebook OAuth2
  • SOCIAL_AUTH_URL_NAMESPACE = 'social' matches URL namespace
  • OAuth keys wired to environment variables
  • manage.py check reports 0 issues
  • URL reverse for all social backends works
  • cloud_ide.login legacy status documented — superseded by social_django
  • Committed and pushed to modernize/django5

Open Steps — Manual OAuth Credential Setup

Before social login can be tested end-to-end, developers must:

  1. Google OAuth2 — Create a project in Google Cloud Console, enable the People API, create OAuth 2.0 credentials, and set the authorized redirect URI to https://<your-domain>/social-auth/complete/google-oauth2/. Then set:

    export GOOGLE_KEY=<client-id>
    export GOOGLE_SECRET=<client-secret>
    
  2. Twitter/X OAuth — Create an app at developer.twitter.com, enable "Sign in with Twitter", set callback URL to https://<your-domain>/social-auth/complete/twitter/. Then set:

    export TWITTER_KEY=<api-key>
    export TWITTER_SECRET=<api-secret>
    
  3. Facebook OAuth2 — Create an app at developers.facebook.com, add Facebook Login product, set redirect URI to https://<your-domain>/social-auth/complete/facebook/. Then set:

    export FACEBOOK_KEY=<app-id>
    export FACEBOOK_SECRET=<app-secret>
    
  4. For local development use http://127.0.0.1:8001 as the domain and register it in each provider's allowed origins.