Merge lp:~dholbach/developer-ubuntu-com/queue-updates-1transaction into lp:developer-ubuntu-com

Proposed by Daniel Holbach on 2015-09-04
Status: Merged
Approved by: David Callé on 2015-09-07
Approved revision: 140
Merged at revision: 145
Proposed branch: lp:~dholbach/developer-ubuntu-com/queue-updates-1transaction
Merge into: lp:developer-ubuntu-com
Diff against target: 184 lines (+51/-25)
1 file modified
developer_portal/management/commands/import-external-docs-branches.py (+51/-25)
To merge this branch: bzr merge lp:~dholbach/developer-ubuntu-com/queue-updates-1transaction
Reviewer Review Type Date Requested Status
David Callé 2015-09-07 Approve on 2015-09-07
Ubuntu App Developer site developers 2015-09-04 Pending
Review via email: mp+270158@code.launchpad.net

Commit Message

Queue create/update/remove transactions and run them in one transaction at the end.

This hopefully avoid writes in different developer.u.c app units.

Description of the Change

Queue create/update/remove transactions and run them in one transaction at the end. This hopefully avoid writes in different developer.u.c app units.

Change logging.ERROR to logging.DEBUG to see DB queries.

To post a comment you must log in.
137. By Daniel Holbach on 2015-09-04

remove old Page.delete() statement

138. By Daniel Holbach on 2015-09-04

use db_actions.remove_page

139. By Daniel Holbach on 2015-09-04

merge trunk

Daniel Holbach (dholbach) wrote :

Test data to use:

sqlite> select * from developer_portal_externaldocsbranch;
1|lp:snapcraft|snappy/snapcraft|intro.md
2|lp:snappy|snappy/guides/devel|
3|lp:snappy/15.04|snappy/guides/current|
sqlite>

Daniel Holbach (dholbach) wrote :

Some more explanation of what's happening:

Add a DBAction class with three methods: add_page() and remove_page() to add actions to the queue and run() to execute the actions. The queue uses a list, so the order is preserved (not that it should matter in our case).

The Page.objects...delete() call is replaced with remove_page().
The get_or_create_page() calls are replaced with add_page() calls.

For convenience, the call to .publish('en') is also moved into DBAction.run().

The constructors of the individual classes are all modified to pass our DBAction queue to all the places where it's required.

Hope that makes the review easier.

140. By Daniel Holbach on 2015-09-07

apply fix from David for the case where there are no pages in the database yet

David Callé (davidc3) wrote :

+1!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'developer_portal/management/commands/import-external-docs-branches.py'
2--- developer_portal/management/commands/import-external-docs-branches.py 2015-08-11 11:50:40 +0000
3+++ developer_portal/management/commands/import-external-docs-branches.py 2015-09-07 13:07:56 +0000
4@@ -1,4 +1,5 @@
5 from django.core.management.base import BaseCommand
6+from django.db import transaction
7
8 from cms.api import create_page, add_plugin
9 from cms.models import Page, Title
10@@ -21,12 +22,34 @@
11 DOCS_DIRNAME = 'docs'
12
13
14+class DBActions:
15+ added_pages = []
16+ removed_pages = []
17+
18+ def add_page(self, **kwargs):
19+ self.added_pages += [kwargs]
20+
21+ def remove_page(self, page_id):
22+ self.removed_pages += [page_id]
23+
24+ @transaction.commit_on_success() # XXX: using=database
25+ def run(self):
26+ for added_page in self.added_pages:
27+ page = get_or_create_page(**added_page)
28+ page.publish('en')
29+
30+ # Only remove pages created by a script!
31+ Page.objects.filter(id__in=self.removed_pages,
32+ created_by="script").delete()
33+
34+
35 class MarkdownFile:
36 html = None
37
38- def __init__(self, fn, docs_namespace, slug_override=None):
39+ def __init__(self, fn, docs_namespace, db_actions, slug_override=None):
40 self.fn = fn
41 self.docs_namespace = docs_namespace
42+ self.db_actions = db_actions
43 if slug_override:
44 self.slug = slug_override
45 else:
46@@ -82,15 +105,14 @@
47
48 def publish(self):
49 '''Publishes pages in their branch alias namespace.'''
50- page = get_or_create_page(
51- self.title, full_url=self.full_url, menu_title=self.title,
52+ self.db_actions.add_page(
53+ title=self.title, full_url=self.full_url, menu_title=self.title,
54 html=self.html)
55- page.publish('en')
56
57
58 class SnappyMarkdownFile(MarkdownFile):
59- def __init__(self, fn, docs_namespace):
60- MarkdownFile.__init__(self, fn, docs_namespace)
61+ def __init__(self, fn, docs_namespace, db_actions):
62+ MarkdownFile.__init__(self, fn, docs_namespace, db_actions)
63 self._make_snappy_mods()
64
65 def _make_snappy_mods(self):
66@@ -112,10 +134,10 @@
67 def publish(self):
68 if self.release_alias == "current":
69 # Add a guides/<page> redirect to guides/current/<page>
70- page = get_or_create_page(
71- self.title, full_url=self.full_url.replace('/current', ''),
72+ self.db_actions.add_page(
73+ title=self.title,
74+ full_url=self.full_url.replace('/current', ''),
75 redirect="/snappy/guides/current/%s" % (self.slug))
76- page.publish('en')
77 else:
78 self.title += " (%s)" % (self.release_alias,)
79 MarkdownFile.publish(self)
80@@ -133,7 +155,7 @@
81 class LocalBranch:
82 titles = {}
83
84- def __init__(self, dirname, external_branch):
85+ def __init__(self, dirname, external_branch, db_actions):
86 self.dirname = dirname
87 self.docs_path = os.path.join(self.dirname, DOCS_DIRNAME)
88 self.doc_fns = glob.glob(self.docs_path+'/*.md')
89@@ -143,6 +165,7 @@
90 self.release_alias = os.path.basename(self.docs_namespace)
91 self.index_doc_title = self.release_alias.title()
92 self.index_doc = self.external_branch.index_doc
93+ self.db_actions = db_actions
94 self.markdown_class = MarkdownFile
95
96 def import_markdown(self):
97@@ -151,10 +174,12 @@
98 md_file = self.markdown_class(
99 doc_fn,
100 os.path.dirname(self.docs_namespace),
101+ self.db_actions,
102 slug_override=os.path.basename(self.docs_namespace))
103 self.md_files.insert(0, md_file)
104 else:
105- md_file = self.markdown_class(doc_fn, self.docs_namespace)
106+ md_file = self.markdown_class(doc_fn, self.docs_namespace,
107+ self.db_actions)
108 self.md_files += [md_file]
109 self.titles[md_file.fn] = md_file.title
110 if not self.index_doc:
111@@ -164,10 +189,11 @@
112 imported_page_urls = set([md_file.full_url
113 for md_file in self.md_files])
114 index_doc = page_resolver.get_page_queryset_from_path(
115- self.docs_namespace)[0]
116- # All pages in this namespace currently in the database
117- db_pages = index_doc.get_descendants().all()
118- delete_pages = []
119+ self.docs_namespace)
120+ db_pages = []
121+ if len(index_doc):
122+ # All pages in this namespace currently in the database
123+ db_pages = index_doc.get_descendants().all()
124 for db_page in db_pages:
125 still_relevant = False
126 for url in imported_page_urls:
127@@ -177,9 +203,7 @@
128 # At this point we know that there's no match and the page
129 # can be deleted.
130 if not still_relevant:
131- delete_pages += [db_page.id]
132- # Only remove pages created by a script!
133- Page.objects.filter(id__in=delete_pages, created_by="script").delete()
134+ self.db_actions.remove_page(db_page.id)
135
136 def publish(self):
137 for md_file in self.md_files:
138@@ -209,16 +233,15 @@
139 "href=\"https://code.launchpad.net/snappy\">%s</a>.</p>\n"
140 "</div></div>") % (self.release_alias, list_pages,
141 self.external_branch.lp_origin)
142- new_release_page = get_or_create_page(
143- self.index_doc_title, full_url=self.docs_namespace,
144+ self.db_actions.add_page(
145+ title=self.index_doc_title, full_url=self.docs_namespace,
146 in_navigation=in_navigation, redirect=redirect, html=landing,
147 menu_title=menu_title)
148- new_release_page.publish('en')
149
150
151 class SnappyLocalBranch(LocalBranch):
152- def __init__(self, dirname, external_branch):
153- LocalBranch.__init__(self, dirname, external_branch)
154+ def __init__(self, dirname, external_branch, db_actions):
155+ LocalBranch.__init__(self, dirname, external_branch, db_actions)
156 self.markdown_class = SnappyMarkdownFile
157 self.index_doc_title = 'Snappy documentation'
158 if self.release_alias != 'current':
159@@ -275,6 +298,7 @@
160 'ExternalDocsBranch table yet.')
161 return
162 tempdir = tempfile.mkdtemp()
163+ db_actions = DBActions()
164 for branch in ExternalDocsBranch.objects.filter(
165 docs_namespace__regex=selection):
166 checkout_location = os.path.join(
167@@ -285,13 +309,15 @@
168 shutil.rmtree(checkout_location)
169 break
170 if branch.lp_origin.startswith('lp:snappy'):
171- local_branch = SnappyLocalBranch(checkout_location, branch)
172+ local_branch = SnappyLocalBranch(checkout_location, branch,
173+ db_actions)
174 else:
175- local_branch = LocalBranch(checkout_location, branch)
176+ local_branch = LocalBranch(checkout_location, branch, db_actions)
177 local_branch.import_markdown()
178 local_branch.publish()
179 local_branch.remove_old_pages()
180 shutil.rmtree(tempdir)
181+ db_actions.run()
182
183
184 class Command(BaseCommand):

Subscribers

People subscribed via source and target branches