|
15 | 15 | from readthedocs.search import tasks
|
16 | 16 | from readthedocs.search.faceted_search import PageSearch
|
17 | 17 |
|
18 |
| -from .serializers import PageSearchSerializer |
| 18 | +from .serializers import PageSearchSerializer, VersionData |
19 | 19 |
|
20 | 20 | log = logging.getLogger(__name__)
|
21 | 21 |
|
@@ -185,67 +185,74 @@ def _validate_query_params(self):
|
185 | 185 |
|
186 | 186 | def _get_all_projects_data(self):
|
187 | 187 | """
|
188 |
| - Return a dict containing the project slug and its version URL and version's doctype. |
189 |
| -
|
190 |
| - The dictionary contains the project and its subprojects. Each project's |
191 |
| - slug is used as a key and a tuple with the documentation URL and doctype |
192 |
| - from the version. Example: |
193 |
| -
|
194 |
| - { |
195 |
| - "requests": ( |
196 |
| - "https://requests.readthedocs.io/en/latest/", |
197 |
| - "sphinx", |
198 |
| - ), |
199 |
| - "requests-oauth": ( |
200 |
| - "https://requests-oauth.readthedocs.io/en/latest/", |
201 |
| - "sphinx_htmldir", |
202 |
| - ), |
203 |
| - } |
| 188 | + Return a dictionary of the project itself and all its subprojects. |
204 | 189 |
|
205 |
| - :rtype: dict |
206 |
| - """ |
207 |
| - all_projects = self._get_all_projects() |
208 |
| - version_slug = self._get_version().slug |
209 |
| - project_urls = {} |
210 |
| - for project in all_projects: |
211 |
| - project_urls[project.slug] = project.get_docs_url(version_slug=version_slug) |
212 |
| - |
213 |
| - versions_doctype = ( |
214 |
| - Version.objects |
215 |
| - .filter(project__slug__in=project_urls.keys(), slug=version_slug) |
216 |
| - .values_list('project__slug', 'documentation_type') |
217 |
| - ) |
| 190 | + Example: |
218 | 191 |
|
219 |
| - projects_data = { |
220 |
| - project_slug: (project_urls[project_slug], doctype) |
221 |
| - for project_slug, doctype in versions_doctype |
222 |
| - } |
223 |
| - return projects_data |
| 192 | + .. code:: |
224 | 193 |
|
225 |
| - def _get_all_projects(self): |
226 |
| - """ |
227 |
| - Returns a list of the project itself and all its subprojects the user has permissions over. |
| 194 | + { |
| 195 | + "requests": VersionData( |
| 196 | + "latest", |
| 197 | + "sphinx", |
| 198 | + "https://requests.readthedocs.io/en/latest/", |
| 199 | + ), |
| 200 | + "requests-oauth": VersionData( |
| 201 | + "latest", |
| 202 | + "sphinx_htmldir", |
| 203 | + "https://requests-oauth.readthedocs.io/en/latest/", |
| 204 | + ), |
| 205 | + } |
| 206 | +
|
| 207 | + .. note:: The response is cached into the instance. |
228 | 208 |
|
229 |
| - :rtype: list |
| 209 | + :rtype: A dictionary of project slugs mapped to a `VersionData` object. |
230 | 210 | """
|
| 211 | + cache_key = '__cached_projects_data' |
| 212 | + projects_data = getattr(self, cache_key, None) |
| 213 | + if projects_data is not None: |
| 214 | + return projects_data |
| 215 | + |
231 | 216 | main_version = self._get_version()
|
232 | 217 | main_project = self._get_project()
|
233 | 218 |
|
234 |
| - all_projects = [main_project] |
| 219 | + projects_data = { |
| 220 | + main_project.slug: VersionData( |
| 221 | + slug=main_version.slug, |
| 222 | + doctype=main_version.documentation_type, |
| 223 | + docs_url=main_project.get_docs_url(version_slug=main_version.slug), |
| 224 | + ) |
| 225 | + } |
235 | 226 |
|
236 | 227 | subprojects = Project.objects.filter(
|
237 | 228 | superprojects__parent_id=main_project.id,
|
238 | 229 | )
|
239 | 230 | for project in subprojects:
|
240 |
| - version = ( |
241 |
| - Version.internal |
242 |
| - .public(user=self.request.user, project=project, include_hidden=False) |
243 |
| - .filter(slug=main_version.slug) |
244 |
| - .first() |
245 |
| - ) |
246 |
| - if version: |
247 |
| - all_projects.append(version.project) |
248 |
| - return all_projects |
| 231 | + version = self._get_subproject_versions_queryset( |
| 232 | + main_version=main_version, |
| 233 | + subproject=project, |
| 234 | + ).first() |
| 235 | + if version and self._has_permission(self.request.user, version): |
| 236 | + url = project.get_docs_url(version_slug=version.slug) |
| 237 | + projects_data[project.slug] = VersionData( |
| 238 | + slug=version.slug, |
| 239 | + doctype=version.documentation_type, |
| 240 | + docs_url=url, |
| 241 | + ) |
| 242 | + |
| 243 | + setattr(self, cache_key, projects_data) |
| 244 | + return projects_data |
| 245 | + |
| 246 | + def _get_subproject_versions_queryset(self, main_version, subproject): |
| 247 | + """Get a queryset with the versions from the subproject that match `main_version`.""" |
| 248 | + return ( |
| 249 | + Version.internal |
| 250 | + .public(user=self.request.user, project=subproject, include_hidden=False) |
| 251 | + .filter(slug=main_version.slug) |
| 252 | + ) |
| 253 | + |
| 254 | + def _has_permission(self, user, version): |
| 255 | + return True |
249 | 256 |
|
250 | 257 | def _record_query(self, response):
|
251 | 258 | project_slug = self._get_project().slug
|
@@ -276,7 +283,7 @@ def get_queryset(self):
|
276 | 283 | is compatible with DRF's paginator.
|
277 | 284 | """
|
278 | 285 | filters = {}
|
279 |
| - filters['project'] = [p.slug for p in self._get_all_projects()] |
| 286 | + filters['project'] = list(self._get_all_projects_data().keys()) |
280 | 287 | filters['version'] = self._get_version().slug
|
281 | 288 |
|
282 | 289 | # Check to avoid searching all projects in case these filters are empty.
|
|
0 commit comments