Merge lp:~dholbach/developer-ubuntu-com/1471160 into lp:developer-ubuntu-com

Proposed by Daniel Holbach
Status: Rejected
Rejected by: Daniel Holbach
Proposed branch: lp:~dholbach/developer-ubuntu-com/1471160
Merge into: lp:developer-ubuntu-com
Diff against target: 341 lines (+293/-0)
5 files modified
developer_portal/admin.py (+7/-0)
developer_portal/management/commands/import-snappy-branches.py (+214/-0)
developer_portal/migrations/0003_add_snappy_docs_branches.py (+60/-0)
developer_portal/models.py (+10/-0)
requirements.txt (+2/-0)
To merge this branch: bzr merge lp:~dholbach/developer-ubuntu-com/1471160
Reviewer Review Type Date Requested Status
Ubuntu App Developer site developers Pending
Review via email: mp+264673@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Holbach (dholbach) wrote :

First cut of importing snappy docs into the developer site.

133. By Daniel Holbach

fix BeautifulSoup initialisation warning

134. By Daniel Holbach

generate cms path from imported snappy file, add comment

135. By Daniel Holbach

fix admin view of SnappyDocsBranch

136. By Daniel Holbach

merged lp:~davidc3/developer-ubuntu-com/1471160_publication

137. By Daniel Holbach

add FIXME entries

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'developer_portal/admin.py'
--- developer_portal/admin.py 2014-12-01 15:11:25 +0000
+++ developer_portal/admin.py 2015-07-16 13:46:03 +0000
@@ -3,6 +3,8 @@
3from reversion.models import Revision, Version3from reversion.models import Revision, Version
4from reversion.admin import VersionAdmin4from reversion.admin import VersionAdmin
55
6from .models import SnappyDocsBranch
7
6__all__ = (8__all__ = (
7)9)
810
@@ -22,3 +24,8 @@
2224
23admin.site.register(Version, VersionAdmin)25admin.site.register(Version, VersionAdmin)
2426
27class SnappyDocsBranchAdmin(admin.ModelAdmin):
28 list_display = ('branch_origin', 'path_alias')
29 list_filter = ('branch_origin', 'path_alias')
30
31admin.site.register(SnappyDocsBranch, SnappyDocsBranchAdmin)
2532
=== added file 'developer_portal/management/commands/import-snappy-branches.py'
--- developer_portal/management/commands/import-snappy-branches.py 1970-01-01 00:00:00 +0000
+++ developer_portal/management/commands/import-snappy-branches.py 2015-07-16 13:46:03 +0000
@@ -0,0 +1,214 @@
1from django.core.management.base import NoArgsCommand
2
3from bs4 import BeautifulSoup
4import codecs
5import glob
6import logging
7import markdown
8import os
9import re
10import shutil
11import subprocess
12import tempfile
13
14from developer_portal.models import SnappyDocsBranch
15
16RELEASE_PAGES = {}
17
18
19class MarkdownFile():
20 html = None
21 cms_path = ''
22
23 def __init__(self, fn):
24 self.fn = fn
25 self.slug = slugify(self.fn)
26 with codecs.open(self.fn, 'r', encoding='utf-8') as f:
27 self.html = markdown.markdown(f.read(), output_format="html5")
28 self.release_alias = self._get_release_alias()
29 self.title = self._read_title()
30 self._remove_body_and_html_tags()
31 self._use_developer_site_style()
32
33 def _get_release_alias(self):
34 alias = re.findall(r'/tmp/tmp\S+?/(\S+?)/docs/\S+?', self.fn)
35 return alias[0]
36
37 def _read_title(self):
38 soup = BeautifulSoup(self.html, 'html5lib')
39 if soup.title:
40 return soup.title.text
41 if soup.h1:
42 return soup.h1.text
43 return slugify(self.fn).replace('-', ' ').title()
44
45 def _remove_body_and_html_tags(self):
46 self.html = re.sub(r"<html>\n\s<body>\n", "", self.html,
47 flags=re.MULTILINE)
48 self.html = re.sub(r"\s<\/body>\n<\/html>", "", self.html,
49 flags=re.MULTILINE)
50
51 def _use_developer_site_style(self):
52 # Make sure the reader knows which documentation she is browsing
53 if self.release_alias != "current":
54 begin = (u"<div class=\"row no-border\">\n"
55 "<div class=\"box pull-three three-col\">"
56 "<p>You are browsing the Snappy <code>%s</code> "
57 "documentation.</p>"
58 "<p><a href=\"/snappy/guides/current/%s\">"
59 "Back to the latest stable release &rsaquo;"
60 "</a></p></div>\n"
61 "<div class=\"eight-col\">\n") % (self.release_alias,
62 self.slug, )
63 else:
64 begin = (u"<div class=\"row no-border\">"
65 "\n<div class=\"eight-col\">\n")
66 end = u"</div>\n</div>"
67 self.html = begin + self.html + end
68 self.html = self.html.replace(
69 "<pre><code>",
70 "</div><div class=\"twelve-col\"><pre><code>")
71 self.html = self.html.replace(
72 "</code></pre>",
73 "</code></pre></div><div class=\"eight-col\">")
74
75 def replace_links(self, titles):
76 for title in titles:
77 url = u"/snappy/guides/%s/%s" % (
78 self.release_alias, slugify(title))
79 link = u"<a href=\"%s\">%s</a>" % (url, titles[title])
80 self.html = self.html.replace(os.path.basename(title), link)
81
82 def publish(self):
83 '''Publishes pages in their branch alias namespace.'''
84 from cms.api import create_page, add_plugin
85
86 page_title = self.title
87
88 if self.release_alias == "current":
89 # Add a guides/<page> redirect to guides/current/<page>
90 page = create_page(
91 self.title, "default.html", "en",
92 slug=self.slug, parent=RELEASE_PAGES['guides_page'],
93 in_navigation=True, position="last-child",
94 redirect="/snappy/guides/current/%s" % (self.slug))
95 page.publish('en')
96 else:
97 page_title += " (%s)" % (self.release_alias,)
98
99 page = create_page(
100 page_title, "default.html", "en", slug=self.slug,
101 menu_title=self.title, parent=RELEASE_PAGES[self.release_alias],
102 in_navigation=True, position="last-child")
103 placeholder = page.placeholders.get()
104 add_plugin(placeholder, 'RawHtmlPlugin', 'en', body=self.html)
105 page.publish('en')
106
107
108def slugify(filename):
109 return os.path.basename(filename).replace('.md', '')
110
111
112def get_branch_from_lp(origin, alias):
113 return subprocess.call([
114 'bzr', 'checkout', '--lightweight', origin, alias])
115
116
117class LocalBranch():
118 titles = {}
119
120 def __init__(self, dirname):
121 self.dirname = dirname
122 self.docs_path = os.path.join(self.dirname, 'docs')
123 self.doc_fns = glob.glob(self.docs_path+'/*.md')
124 self.md_files = []
125
126 def import_markdown(self):
127 for doc_fn in self.doc_fns:
128 md_file = MarkdownFile(doc_fn)
129 self.md_files += [md_file]
130 self.titles[md_file.fn] = md_file.title
131 for md_file in self.md_files:
132 md_file.replace_links(self.titles)
133 md_file.publish()
134
135
136# FIXME:
137# - we retrieve the old article somehow
138# - then find the Raw HTML plugin and
139# - replace the html in there
140# - also: remove pages we don't need anymore
141# - add new ones
142# - make sure we can do that for different sets of docs with different pages
143#
144def remove_old_pages():
145 '''Removes all pages in snappy/guides, created by the importer.'''
146 from cms.models import Title, Page
147
148 pages_to_remove = []
149 for g in Title.objects.select_related('page__id').filter(
150 path__regex="snappy/guides/.*"):
151 pages_to_remove.append(g.page.id)
152 Page.objects.filter(id__in=pages_to_remove, created_by="script").delete()
153
154
155def refresh_landing_page(release_alias):
156 '''Creates a branch page at snappy/guides/<branch alias>.'''
157 from cms.api import create_page
158 from cms.models import Title
159
160 guides_page = Title.objects.filter(
161 path="snappy/guides", published=True,
162 language="en", publisher_is_draft=True)[0]
163 RELEASE_PAGES['guides_page'] = guides_page.page
164
165 if release_alias == "current":
166 redirect = "/snappy/guides"
167 else:
168 redirect = None
169 new_release_page = create_page(
170 release_alias, "default.html", "en", slug=release_alias,
171 parent=RELEASE_PAGES['guides_page'], in_navigation=False,
172 position="last-child", redirect=redirect)
173 # FIXME Page needs content
174 # placeholder = new_release_page.placeholders.get()
175 # add_plugin(placeholder, 'RawHtmlPlugin', 'en', body="<html goes here>")
176 new_release_page.publish('en')
177 RELEASE_PAGES[release_alias] = new_release_page
178
179
180def import_branches():
181 if not SnappyDocsBranch.objects.count():
182 logging.error('No Snappy branches registered in the '
183 'SnappyDocsBranch table yet.')
184 return
185 # FIXME: Do the removal part last. Else we might end up in situations
186 # where some code breaks and we stay in a state without articles.
187 remove_old_pages()
188 tempdir = tempfile.mkdtemp()
189 pwd = os.getcwd()
190 os.chdir(tempdir)
191 for branch in SnappyDocsBranch.objects.all():
192 if get_branch_from_lp(branch.branch_origin, branch.path_alias) != 0:
193 logging.error(
194 'Could not check out branch "%s".' % branch.branch_origin)
195 shutil.rmtree(os.path.join(tempdir, branch.path_alias))
196 break
197 refresh_landing_page(branch.path_alias)
198 os.chdir(pwd)
199 for local_branch in [a for a in glob.glob(tempdir+'/*')
200 if os.path.isdir(a)]:
201 branch = LocalBranch(local_branch)
202 branch.import_markdown()
203 shutil.rmtree(tempdir)
204
205
206class Command(NoArgsCommand):
207 help = "Import Snappy branches for documentation."
208
209 def handle_noargs(self, **options):
210 logging.basicConfig(
211 level=logging.DEBUG,
212 format='%(asctime)s %(levelname)-8s %(message)s',
213 datefmt='%F %T')
214 import_branches()
0215
=== added file 'developer_portal/migrations/0003_add_snappy_docs_branches.py'
--- developer_portal/migrations/0003_add_snappy_docs_branches.py 1970-01-01 00:00:00 +0000
+++ developer_portal/migrations/0003_add_snappy_docs_branches.py 2015-07-16 13:46:03 +0000
@@ -0,0 +1,60 @@
1# -*- coding: utf-8 -*-
2from south.utils import datetime_utils as datetime
3from south.db import db
4from south.v2 import SchemaMigration
5from django.db import models
6
7
8class Migration(SchemaMigration):
9
10 def forwards(self, orm):
11 # Adding model 'SnappyDocsBranch'
12 db.create_table(u'developer_portal_snappydocsbranch', (
13 (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14 ('branch_origin', self.gf('django.db.models.fields.CharField')(max_length=200)),
15 ('path_alias', self.gf('django.db.models.fields.CharField')(max_length=20)),
16 ))
17 db.send_create_signal(u'developer_portal', ['SnappyDocsBranch'])
18
19
20 def backwards(self, orm):
21 # Deleting model 'SnappyDocsBranch'
22 db.delete_table(u'developer_portal_snappydocsbranch')
23
24
25 models = {
26 'cms.cmsplugin': {
27 'Meta': {'object_name': 'CMSPlugin'},
28 'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
29 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
30 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
31 'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
32 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
33 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
34 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
35 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
36 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
37 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
38 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
39 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
40 },
41 'cms.placeholder': {
42 'Meta': {'object_name': 'Placeholder'},
43 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
44 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
45 'slot': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
46 },
47 u'developer_portal.rawhtml': {
48 'Meta': {'object_name': 'RawHtml'},
49 'body': ('django.db.models.fields.TextField', [], {}),
50 u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'})
51 },
52 u'developer_portal.snappydocsbranch': {
53 'Meta': {'object_name': 'SnappyDocsBranch'},
54 'branch_origin': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
55 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
56 'path_alias': ('django.db.models.fields.CharField', [], {'max_length': '20'})
57 }
58 }
59
60 complete_apps = ['developer_portal']
0\ No newline at end of file61\ No newline at end of file
162
=== modified file 'developer_portal/models.py'
--- developer_portal/models.py 2015-01-19 16:29:47 +0000
+++ developer_portal/models.py 2015-07-16 13:46:03 +0000
@@ -1,3 +1,5 @@
1from django.db import models
2from django.utils.translation import ugettext_lazy as _
13
2from cms.models import CMSPlugin4from cms.models import CMSPlugin
3from djangocms_text_ckeditor.html import extract_images5from djangocms_text_ckeditor.html import extract_images
@@ -13,3 +15,11 @@
13 body = extract_images(body, self)15 body = extract_images(body, self)
14 self.body = body16 self.body = body
15 AbstractText.save(self, *args, **kwargs)17 AbstractText.save(self, *args, **kwargs)
18
19
20class SnappyDocsBranch(models.Model):
21 branch_origin = models.CharField(max_length=200,
22 help_text=_('Launchpad branch location, ie: lp:snappy/15.04'))
23 path_alias = models.CharField(max_length=20,
24 help_text=_('Path alias we want to use for the docs, '
25 'ie "15.04" or "latest", etc.'))
1626
=== modified file 'requirements.txt'
--- requirements.txt 2015-02-26 15:53:30 +0000
+++ requirements.txt 2015-07-16 13:46:03 +0000
@@ -1,6 +1,8 @@
1Django==1.6.81Django==1.6.8
2Markdown==2.6.2
2South==1.0.13South==1.0.1
3Pillow==2.6.14Pillow==2.6.1
5beautifulsoup4==4.4.0
4cmsplugin-zinnia==0.66cmsplugin-zinnia==0.6
5dj-database-url==0.3.07dj-database-url==0.3.0
6django-admin-enhancer==0.1.3.18django-admin-enhancer==0.1.3.1

Subscribers

People subscribed via source and target branches