def _create(resource, error=None, metadata=None, body_stream=None): self = ResourceRevision() self.resource = resource self.error = error self.metadata = metadata # (self._id computed below) self.has_body = body_stream is not None project = self.project # Need to do this first to get the database ID def fg_task(): RR = ResourceRevision c = project._db.cursor() c.execute('insert into resource_revision (resource_id, error, metadata) values (?, ?, ?)', (resource._id, RR._encode_error(error), RR._encode_metadata(metadata))) project._db.commit() self._id = c.lastrowid fg_call_and_wait(fg_task) if body_stream: try: body_filepath = os.path.join(project.path, Project._RESOURCE_REVISION_DIRNAME, str(self._id)) with open(body_filepath, 'wb') as body_file: shutil.copyfileobj(body_stream, body_file) except: # Rollback database commit def fg_task(): c = project._db.cursor() c.execute('delete from resource_revision where id=?', (self._id,)) project._db.commit() fg_call_and_wait(fg_task) raise return self
def __call__(self): # If the resource is already up-to-date, return its default revision def fg_task(): if self._resource.up_to_date(): return self._resource.default_revision() else: return None body_revision = fg_call_and_wait(fg_task) if body_revision is not None: return body_revision # TODO: Report errors (embedded in the ResourceRevision) using the completion subtitle. # Need to add support for this behavior to Task. try: from crystal.download import download_resource_revision body_revision = download_resource_revision(self._resource, self) # Automatically parse the body's links and create associated resources self.subtitle = 'Parsing links...' r = self._resource links = body_revision.links() urls = [urlparse.urljoin(r.url, link.relative_url) for link in links] self.subtitle = 'Recording links...' def fg_task(): # PERF: This is taking 1-2 sec to execute on THEM's Review List page. # Need to look at optimizing this to avoid blocking the UI. for url in urls: Resource(r.project, url) fg_call_and_wait(fg_task) return body_revision finally: self.subtitle = 'Waiting before performing next request...' sleep(_DELAY_BETWEEN_DOWNLOADS)
def do_GET(self): request_host = self.request_host request_url = 'http://%s%s' % (request_host, self.path) scheme_rest_match = _SCHEME_REST_RE.match(self.path) if not scheme_rest_match: path_parts = urlparse.urlparse(self.path) if path_parts.path == '/': query_params = urlparse.parse_qs(path_parts.query) else: query_params = {} self.send_welcome_page(query_params) return (scheme, rest) = scheme_rest_match.groups() archive_url = '%s://%s' % (scheme, rest) # TODO: Normalize archive url by stripping fragment (and maybe also {params, query}). # This should probably be implemented in a static method on Resource, # as this functionality should also be used by the Resource constructor. resource = self.project.get_resource(archive_url) if not resource: self.send_resource_not_in_archive(archive_url) return revision = fg_call_and_wait(resource.default_revision) if not revision: self.send_resource_not_in_archive(archive_url) return self.send_revision(revision)
def bg_task(): while True: def fg_task(): return (task.try_get_next_task_unit(), task.complete) (unit, task_complete) = fg_call_and_wait(fg_task) if unit is None: if task_complete: break else: sleep(_ROOT_TASK_POLL_INTERVAL) continue unit() # Run unit directly on this bg thread