def storage_used(self): """ Bytes of storage used by main files and compressed file if any """ storage_used = ProjectFiles().published_project_storage_used(self) zip_file_size = ProjectFiles().get_zip_file_size(self) return storage_used, zip_file_size
def display_project_file(request, project, file_path): """ Display a file from either a published or unpublished project. The user is assumed to be authorized to view the project. file_path is the name of the file relative to project.file_root(). """ try: abs_path = os.path.join(project.file_root(), file_path) infile = ProjectFiles().open(abs_path) except IsADirectoryError: return redirect(request.path + '/') except (FileNotFoundError, NotADirectoryError): raise Http404() except (IOError, OSError) as err: raise (Http404() if err.errno == ENAMETOOLONG else err) if file_path.endswith('.csv.gz'): cls = GzippedCSVFileView else: (_, suffix) = os.path.splitext(file_path) cls = _suffixes.get(suffix, TextFileView) view = cls(project, file_path, infile) return view.render(request)
def file_root(self): """ Root directory where the main user uploaded files are located """ return ProjectFiles().get_file_root(self.slug, self.version, self.access_policy, PublishedProject)
def perform_action(self): """ Move the items into the selected directory """ errors = ErrorList() dest = self.cleaned_data['destination_folder'] for item in self.cleaned_data['items']: path = os.path.join(self.file_dir, item) try: ProjectFiles().mv(path, self.dest_dir) except FileExistsError: errors.append( format_html( 'Item named <i>{}</i> already exists in <i>{}</i>', item, dest)) except OSError: if not os.path.exists(path): errors.append( format_html('Item named <i>{}</i> does not exist', item)) else: errors.append( format_html('Unable to move <i>{}</i> into <i>{}</i>', item, dest)) return 'Your items have been moved', errors
def get_directory_content(self, subdir=''): """ Return information for displaying files and directories from the project's file root. """ inspect_dir = self.get_inspect_dir(subdir) return ProjectFiles().get_project_directory_content( inspect_dir, subdir, self.file_display_url, self.file_url)
def raw_url(self): """ Return a URL that can be used to view the file. This URL points to the raw file and will be displayed according to the browser's default settings for the corresponding content type. """ return ProjectFiles().raw_url(self.project, self.path)
def download_url(self): """ Return a URL that can be used to download the file. This URL points to the raw file, but with an extra query parameter indicating that we should try to force the browser to save the file rather than displaying it. """ return ProjectFiles().download_url(self.project, self.path)
def create_license_file(self): """ Create a file containing the text of the project license. A file 'LICENSE.txt' is created at the top level of the project directory, replacing any existing file with that name. """ fname = os.path.join(self.file_root(), 'LICENSE.txt') ProjectFiles().fwrite(fname, self.license_content(fmt='text'))
def project_file_root(self): """ Root directory containing the published project's files. This is the parent directory of the main and special file directories. """ return ProjectFiles().get_project_file_root(self.slug, self.version, self.access_policy, PublishedProject)
def __init__(self, project, *args, **kwargs): super().__init__(*args, **kwargs) self.project = project # No option to set slug if publishing new version if self.project.version_order: del (self.fields['slug']) else: self.fields['slug'].initial = project.slug if not ProjectFiles().can_make_zip(): self.fields['make_zip'].disabled = True self.fields['make_zip'].required = False self.fields['make_zip'].initial = 0
def storage_used(self): """ Total storage used in bytes. This includes the total size of new files uploaded to this project, as well as the total size of files published in past versions of this CoreProject. (The QuotaManager should ensure that the same file is not counted twice in this total.) """ current = ProjectFiles().active_project_storage_used(self) published = self.core_project.total_published_size return current + published
def perform_action(self): """ Create the folder """ errors = ErrorList() name = self.cleaned_data['folder_name'] file_path = os.path.join(self.file_dir, name) try: ProjectFiles().mkdir(file_path) except FileExistsError: errors.append( format_html('Item named <i>{}</i> already exists', name)) except OSError: errors.append(format_html('Unable to create <i>{}</i>', name)) return 'Your folder has been created', errors
def perform_action(self): """ Upload the files """ errors = ErrorList() for file in self.files.getlist('file_field'): try: ProjectFiles().fput(self.file_dir, file) except FileExistsError: errors.append( format_html('Item named <i>{}</i> already exists', file.name)) except OSError: errors.append( format_html('Unable to upload <i>{}</i>', file.name)) return 'Your files have been uploaded', errors
def perform_action(self): """ Delete the items """ errors = ErrorList() for item in self.cleaned_data['items']: path = os.path.join(self.file_dir, item) try: ProjectFiles().rm(path) except OSError as e: if not os.path.exists(path): errors.append( format_html('Item named <i>{}</i> did not exist', item)) else: errors.append( format_html( 'Unable to delete <i>{}</i>', os.path.relpath(e.filename or path, self.file_dir))) return 'Your items have been deleted', errors
def home(request): """ Homepage """ featured = PublishedProject.objects.filter( featured__isnull=False).order_by('featured')[:6] latest = PublishedProject.objects.filter( is_latest_version=True).order_by('-publish_datetime')[:6] news_pieces = News.objects.all().order_by('-publish_datetime')[:5] front_page_banner = News.objects.filter(front_page_banner=True) return render( request, 'home.html', { 'featured': featured, 'latest': latest, 'news_pieces': news_pieces, 'front_page_banner': front_page_banner, 'is_lightwave_supported': ProjectFiles().is_lightwave_supported(), }, )
def save(self): project = super().save(commit=False) # Set the core project and slug core_project = CoreProject.objects.create() project.core_project = core_project slug = get_random_string(20) while exists_project_slug(slug): slug = get_random_string(20) project.slug = slug project.save() # Create the author object for the user author = Author.objects.create( project=project, user=self.user, display_order=1, corresponding_email=self.user.get_primary_email(), is_submitting=True, is_corresponding=True) author.import_profile_info() # Create file directory ProjectFiles().mkdir(project.file_root()) return project
def perform_action(self): """ Rename the items """ errors = ErrorList() old_name = self.cleaned_data['items'][0] new_name = self.cleaned_data['new_name'] old_path = os.path.join(self.file_dir, old_name) new_path = os.path.join(self.file_dir, new_name) try: ProjectFiles().rename(old_path, new_path) except FileExistsError: errors.append( format_html('Item named <i>{}</i> already exists', new_name)) except FileNotFoundError: errors.append( format_html('Item named <i>{}</i> does not exist', old_name)) except OSError: errors.append( format_html('Unable to rename <i>{}</i> to <i>{}</i>', old_name, new_name)) return 'Your item has been renamed', errors
def content_index(request, resource_type=None): """ List of all published resources """ LABELS = { 0: ['Database', 'databases'], 1: ['Software', 'softwares'], 2: ['Challenge', 'challenges'], 3: ['Model', 'models'], } # PROJECT TYPE FILTER form_type = forms.ProjectTypeForm() if 'types' in request.GET: form_type = forms.ProjectTypeForm(request.GET) if form_type.is_valid(): resource_type = [int(t) for t in form_type.cleaned_data['types']] else: resource_type = list(LABELS.keys()) elif resource_type is None: resource_type = list(LABELS.keys()) form_type = forms.ProjectTypeForm({'types': resource_type}) else: resource_type = [resource_type] form_type = forms.ProjectTypeForm({'types': resource_type}) # SORT PROJECTS orderby, direction = 'relevance', 'desc' form_order = forms.ProjectOrderForm() if 'orderby' in request.GET: form_order = forms.ProjectOrderForm(request.GET) if form_order.is_valid(): orderby, direction = form_order.cleaned_data['orderby'].split('-') # TOPIC SEARCH topic = '' if 'topic' in request.GET: form_topic = forms.TopicSearchForm(request.GET) if form_topic.is_valid(): topic = form_topic.cleaned_data['topic'] else: form_topic = forms.TopicSearchForm() # BUILD published_projects = get_content(resource_type=resource_type, orderby=orderby, direction=direction, search_term=topic) # PAGINATION projects = paginate(request, published_projects, 10) params = request.GET.copy() # Remove the page argument from the querystring if it exists try: params.pop('page') except KeyError: pass querystring = params.urlencode() return render( request, 'search/content_index.html', { 'form_order': form_order, 'projects': projects, 'form_type': form_type, 'form_topic': form_topic, 'querystring': querystring, 'is_lightwave_supported': ProjectFiles().is_lightwave_supported(), }, )
def has_wfdb(self): """ Whether the project has wfdb files. """ return ProjectFiles().has_wfdb_files(self)
name='moody_challenge'), path('about/challenge/community-challenge', views.community_challenge, name='community_challenge'), # path for about static pages path('about/', views.static_view, name='static_view'), path('about/<path:static_url>/', views.static_view, name='static_view'), # robots.txt for crawlers path('robots.txt', lambda x: HttpResponse("User-Agent: *\\Allow: /", content_type="text/plain"), name="robots_file"), ] if ProjectFiles().is_lightwave_supported: urlpatterns.append(path('lightwave/', include('lightwave.urls'))) # backward compatibility for LightWAVE urlpatterns.append( path('cgi-bin/lightwave', lightwave_views.lightwave_server)) if settings.ENABLE_SSO: urlpatterns.append(path('', include('sso.urls'))) if settings.DEBUG: import debug_toolbar # debug toolbar urlpatterns.append(path('__debug__/', include(debug_toolbar.urls)))
def test_project_files_if_google_cloud_storage_type(self): self.assertIsInstance(ProjectFiles(), GCSProjectFiles)
def test_project_files_if_local_storage_type(self): self.assertIsInstance(ProjectFiles(), LocalProjectFiles)
def publish(self, slug=None, make_zip=True, title=None): """ Create a published version of this project and update the submission status. Parameters ---------- slug : the desired custom slug of the published project. make_zip : whether to make a zip of all the files. """ if not self.is_publishable(): raise Exception('The project is not publishable') published_project = PublishedProject(has_wfdb=self.has_wfdb()) # Direct copy over fields for field in [f.name for f in Metadata._meta.fields ] + [f.name for f in SubmissionInfo._meta.fields]: setattr(published_project, field, getattr(self, field)) published_project.slug = slug or self.slug # Create project file root if this is first version or the first # version with a different access policy ProjectFiles().publish_initial(self, published_project) try: with transaction.atomic(): # If this is a new version, previous fields need to be updated # and slug needs to be carried over if self.version_order: previous_published_projects = self.core_project.publishedprojects.all( ) slug = previous_published_projects.first().slug title = previous_published_projects.first().title if slug != published_project.slug: raise ValueError({ "message": "The published project has different slugs." }) # Set the slug if specified published_project.slug = slug or self.slug published_project.title = title or self.title published_project.doi = self.doi # Change internal links (that point to files within # the active project) to point to their new locations # in the published project published_project.update_internal_links(old_project=self) published_project.save() # If this is a new version, all version fields have to be updated if self.version_order > 0: published_project.set_version_order() # Same content, different objects. for reference in self.references.all().order_by('id'): published_reference = PublishedReference.objects.create( description=reference.description, project=published_project) for publication in self.publications.all(): published_publication = PublishedPublication.objects.create( citation=publication.citation, url=publication.url, project=published_project) published_project.set_topics( [t.description for t in self.topics.all()]) for parent_project in self.parent_projects.all(): published_project.parent_projects.add(parent_project) if self.resource_type.id == 1: languages = self.programming_languages.all() if languages: published_project.programming_languages.add( *list(languages)) for author in self.authors.all(): author_profile = author.user.profile published_author = PublishedAuthor.objects.create( project=published_project, user=author.user, is_submitting=author.is_submitting, is_corresponding=author.is_corresponding, approval_datetime=author.approval_datetime, display_order=author.display_order, first_names=author_profile.first_names, last_name=author_profile.last_name, ) affiliations = author.affiliations.all() for affiliation in affiliations: published_affiliation = PublishedAffiliation.objects.create( name=affiliation.name, author=published_author) UploadedDocument.objects.filter( object_id=self.pk, content_type=ContentType.objects.get_for_model( ActiveProject)).update( object_id=published_project.pk, content_type=ContentType.objects.get_for_model( PublishedProject), ) if author.is_corresponding: published_author.corresponding_email = author.corresponding_email.email published_author.save() Contact.objects.create( name=author.get_full_name(), affiliations='; '.join(a.name for a in affiliations), email=author.corresponding_email, project=published_project) # Move the edit and copyedit logs for edit_log in self.edit_logs.all(): edit_log.project = published_project edit_log.save() for copyedit_log in self.copyedit_logs.all(): copyedit_log.project = published_project copyedit_log.save() published_project.required_trainings.set( self.required_trainings.all()) # Set files read only and make zip file if requested move_files_as_readonly( published_project.id, self.file_root(), published_project.file_root(), make_zip, verbose_name='Read Only Files - {}'.format( published_project), ) # Remove the ActiveProject self.delete() except BaseException: ProjectFiles().publish_rollback(self, published_project) raise ProjectFiles().publish_complete(self, published_project) return published_project
class ActiveProject(Metadata, UnpublishedProject, SubmissionInfo): """ The project used for submitting The submission_status field: - 0 : Not submitted - 10 : Submitting author submits. Awaiting editor assignment. - 20 : Editor assigned. Awaiting editor decision. - 30 : Revisions requested. Waiting for resubmission. Loops back to 20 when author resubmits. - 40 : Accepted. In copyedit stage. Awaiting editor to copyedit. - 50 : Editor completes copyedit. Awaiting authors to approve. - 60 : Authors approve copyedit. Ready for editor to publish """ submission_status = models.PositiveSmallIntegerField(default=0) # Max number of active submitting projects a user is allowed to have MAX_SUBMITTING_PROJECTS = 10 INDIVIDUAL_FILE_SIZE_LIMIT = 10 * 1024**3 # Where all the active project files are kept FILE_ROOT = os.path.join(ProjectFiles().file_root, 'active-projects') REQUIRED_FIELDS = ( # 0: Database ('title', 'abstract', 'background', 'methods', 'content_description', 'usage_notes', 'conflicts_of_interest', 'version', 'license', 'short_description'), # 1: Software ('title', 'abstract', 'background', 'content_description', 'usage_notes', 'installation', 'conflicts_of_interest', 'version', 'license', 'short_description'), # 2: Challenge ('title', 'abstract', 'background', 'methods', 'content_description', 'usage_notes', 'conflicts_of_interest', 'version', 'license', 'short_description'), # 3: Model ('title', 'abstract', 'background', 'methods', 'content_description', 'usage_notes', 'installation', 'conflicts_of_interest', 'version', 'license', 'short_description'), ) # Custom labels that don't match model field names LABELS = ( # 0: Database { 'content_description': 'Data Description' }, # 1: Software { 'content_description': 'Software Description', 'methods': 'Technical Implementation', 'installation': 'Installation and Requirements' }, # 2: Challenge { 'background': 'Objective', 'methods': 'Participation', 'content_description': 'Data Description', 'usage_notes': 'Evaluation' }, # 3: Model { 'content_description': 'Model Description', 'methods': 'Technical Implementation', 'installation': 'Installation and Requirements' }, ) SUBMISSION_STATUS_LABELS = { 0: 'Not submitted.', 10: 'Awaiting editor assignment.', 20: 'Awaiting editor decision.', 30: 'Revisions requested.', 40: 'Submission accepted; awaiting editor copyedits.', 50: 'Awaiting authors to approve publication.', 60: 'Awaiting editor to publish.', } class Meta: default_permissions = ('change', ) permissions = [('can_assign_editor', 'Can assign editor')] def storage_used(self): """ Total storage used in bytes. This includes the total size of new files uploaded to this project, as well as the total size of files published in past versions of this CoreProject. (The QuotaManager should ensure that the same file is not counted twice in this total.) """ current = ProjectFiles().active_project_storage_used(self) published = self.core_project.total_published_size return current + published def storage_allowance(self): """ Storage allowed in bytes """ return self.core_project.storage_allowance def get_inspect_dir(self, subdir): """ Return the folder to inspect if valid. subdir joined onto the file root of this project. """ # Sanitize subdir for illegal characters validate_subdir(subdir) # Folder must be a subfolder of the file root # (but not necessarily exist or be a directory) inspect_dir = os.path.join(self.file_root(), subdir) if inspect_dir.startswith(self.file_root()): return inspect_dir else: raise Exception('Invalid directory request') def file_url(self, subdir, file): """ Url of a file to download in this project """ return reverse('serve_active_project_file', args=(self.slug, os.path.join(subdir, file))) def file_display_url(self, subdir, file): """ URL of a file to display in this project """ return reverse('display_active_project_file', args=(self.slug, os.path.join(subdir, file))) def under_submission(self): """ Whether the project is under submission """ return bool(self.submission_status) def submission_deadline(self): return self.creation_datetime + timedelta(days=180) def submission_days_remaining(self): return (self.submission_deadline() - timezone.now()).days def submission_status_label(self): return ActiveProject.SUBMISSION_STATUS_LABELS[self.submission_status] def author_editable(self): """ Whether the project can be edited by its authors """ if self.submission_status in [0, 30]: return True def copyeditable(self): """ Whether the project can be copyedited """ if self.submission_status == 40: return True def archive(self, archive_reason): """ Archive the project. Create an ArchivedProject object, copy over the fields, and delete this object """ archived_project = ArchivedProject(archive_reason=archive_reason, slug=self.slug) modified_datetime = self.modified_datetime # Direct copy over fields for attr in [f.name for f in Metadata._meta.fields ] + [f.name for f in SubmissionInfo._meta.fields]: setattr(archived_project, attr, getattr(self, attr)) archived_project.save() # Redirect the related objects for reference in self.references.all(): reference.project = archived_project reference.save() for publication in self.publications.all(): publication.project = archived_project publication.save() for topic in self.topics.all(): topic.project = archived_project topic.save() for author in self.authors.all(): author.project = archived_project author.save() for edit_log in self.edit_logs.all(): edit_log.project = archived_project edit_log.save() for copyedit_log in self.copyedit_logs.all(): copyedit_log.project = archived_project copyedit_log.save() for parent_project in self.parent_projects.all(): archived_project.parent_projects.add(parent_project) UploadedDocument.objects.filter( object_id=self.pk, content_type=ContentType.objects.get_for_model(ActiveProject) ).update( object_id=archived_project.pk, content_type=ContentType.objects.get_for_model(ArchivedProject)) if self.resource_type.id == 1: languages = self.programming_languages.all() if languages: archived_project.programming_languages.add(*list(languages)) # Voluntary delete if archive_reason == 1: self.clear_files() else: # Move over files os.rename(self.file_root(), archived_project.file_root()) # Copy the ActiveProject timestamp to the ArchivedProject. # Since this is an auto_now field, save() doesn't allow # setting an arbitrary value. queryset = ArchivedProject.objects.filter(id=archived_project.id) queryset.update(modified_datetime=modified_datetime) return self.delete() def fake_delete(self): """ Appear to delete this project. Actually archive it. """ self.archive(archive_reason=1) def check_integrity(self): """ Run integrity tests on metadata fields and return whether the project passes the checks """ self.integrity_errors = ErrorList() # Invitations for invitation in self.authorinvitations.filter(is_active=True): self.integrity_errors.append( 'Outstanding author invitation to {0}'.format( invitation.email)) # Storage requests for storage_request in self.storagerequests.filter(is_active=True): self.integrity_errors.append('Outstanding storage request') # Authors for author in self.authors.all().order_by('display_order'): if not author.get_full_name(): self.integrity_errors.append( 'Author {0} has not fill in name'.format( author.user.username)) if not author.affiliations.all(): self.integrity_errors.append( 'Author {0} has not filled in affiliations'.format( author.user.username)) # Metadata for attr in ActiveProject.REQUIRED_FIELDS[self.resource_type.id]: value = getattr(self, attr) text = unescape(strip_tags(str(value))) if value is None or not text or text.isspace(): l = self.LABELS[ self.resource_type.id][attr] if attr in self.LABELS[ self.resource_type.id] else attr.title().replace( '_', ' ') self.integrity_errors.append( 'Missing required field: {0}'.format(l)) # Ethics if not self.ethics_statement: self.integrity_errors.append( 'Missing required field: Ethics Statement') published_projects = self.core_project.publishedprojects.all() if published_projects: published_versions = [p.version for p in published_projects] if self.version in published_versions: self.integrity_errors.append( 'The version matches a previously published version.') self.version_clash = True else: self.version_clash = False if self.access_policy != AccessPolicy.OPEN and self.dua is None: self.integrity_errors.append( 'You have to choose one of the data use agreements.') if self.integrity_errors: return False else: return True def is_submittable(self): """ Whether the project can be submitted """ return (not self.under_submission() and self.check_integrity()) def submit(self, author_comments): """ Submit the project for review. """ if not self.is_submittable(): raise Exception('ActiveProject is not submittable') self.submission_status = 10 self.submission_datetime = timezone.now() self.author_comments = author_comments self.save() # Create the first edit log EditLog.objects.create(project=self, author_comments=author_comments) def set_submitting_author(self): """ Used to save query time in templates """ self.submitting_author = self.submitting_author() def assign_editor(self, editor): """ Assign an editor to the project and set the submission status to the edit stage. """ self.editor = editor self.submission_status = 20 self.editor_assignment_datetime = timezone.now() self.save() def reassign_editor(self, editor): """ Reassign the current project editor with new editor """ self.editor = editor self.save() def reject(self): """ Reject a project under submission """ self.archive(archive_reason=3) def is_resubmittable(self): """ Submit the project for review. """ return (self.submission_status == 30 and self.check_integrity()) def resubmit(self, author_comments): """ """ if not self.is_resubmittable(): raise Exception('ActiveProject is not resubmittable') with transaction.atomic(): self.submission_status = 20 self.resubmission_datetime = timezone.now() self.save() # Create a new edit log EditLog.objects.create(project=self, is_resubmission=True, author_comments=author_comments) def reopen_copyedit(self): """ Reopen the project for copyediting """ if self.submission_status == 50: self.submission_status = 40 self.copyedit_completion_datetime = None self.save() CopyeditLog.objects.create(project=self, is_reedit=True) self.authors.all().update(approval_datetime=None) def approve_author(self, author): """" Approve an author. Move the project into the next state if the author is the final outstanding one. Return whether the process was successful. """ if self.submission_status == 50 and not author.approval_datetime: now = timezone.now() author.approval_datetime = now author.save() if self.all_authors_approved(): self.author_approval_datetime = now self.submission_status = 60 self.save() return True def all_authors_approved(self): """ Whether all authors have approved the publication """ authors = self.authors.all() return len(authors) == len( authors.filter(approval_datetime__isnull=False)) def is_publishable(self): """ Check whether a project may be published """ if self.submission_status == 60 and self.check_integrity( ) and self.all_authors_approved(): return True return False def clear_files(self): """ Delete the project file directory """ ProjectFiles().rmtree(self.file_root()) def publish(self, slug=None, make_zip=True, title=None): """ Create a published version of this project and update the submission status. Parameters ---------- slug : the desired custom slug of the published project. make_zip : whether to make a zip of all the files. """ if not self.is_publishable(): raise Exception('The project is not publishable') published_project = PublishedProject(has_wfdb=self.has_wfdb()) # Direct copy over fields for field in [f.name for f in Metadata._meta.fields ] + [f.name for f in SubmissionInfo._meta.fields]: setattr(published_project, field, getattr(self, field)) published_project.slug = slug or self.slug # Create project file root if this is first version or the first # version with a different access policy ProjectFiles().publish_initial(self, published_project) try: with transaction.atomic(): # If this is a new version, previous fields need to be updated # and slug needs to be carried over if self.version_order: previous_published_projects = self.core_project.publishedprojects.all( ) slug = previous_published_projects.first().slug title = previous_published_projects.first().title if slug != published_project.slug: raise ValueError({ "message": "The published project has different slugs." }) # Set the slug if specified published_project.slug = slug or self.slug published_project.title = title or self.title published_project.doi = self.doi # Change internal links (that point to files within # the active project) to point to their new locations # in the published project published_project.update_internal_links(old_project=self) published_project.save() # If this is a new version, all version fields have to be updated if self.version_order > 0: published_project.set_version_order() # Same content, different objects. for reference in self.references.all().order_by('id'): published_reference = PublishedReference.objects.create( description=reference.description, project=published_project) for publication in self.publications.all(): published_publication = PublishedPublication.objects.create( citation=publication.citation, url=publication.url, project=published_project) published_project.set_topics( [t.description for t in self.topics.all()]) for parent_project in self.parent_projects.all(): published_project.parent_projects.add(parent_project) if self.resource_type.id == 1: languages = self.programming_languages.all() if languages: published_project.programming_languages.add( *list(languages)) for author in self.authors.all(): author_profile = author.user.profile published_author = PublishedAuthor.objects.create( project=published_project, user=author.user, is_submitting=author.is_submitting, is_corresponding=author.is_corresponding, approval_datetime=author.approval_datetime, display_order=author.display_order, first_names=author_profile.first_names, last_name=author_profile.last_name, ) affiliations = author.affiliations.all() for affiliation in affiliations: published_affiliation = PublishedAffiliation.objects.create( name=affiliation.name, author=published_author) UploadedDocument.objects.filter( object_id=self.pk, content_type=ContentType.objects.get_for_model( ActiveProject)).update( object_id=published_project.pk, content_type=ContentType.objects.get_for_model( PublishedProject), ) if author.is_corresponding: published_author.corresponding_email = author.corresponding_email.email published_author.save() Contact.objects.create( name=author.get_full_name(), affiliations='; '.join(a.name for a in affiliations), email=author.corresponding_email, project=published_project) # Move the edit and copyedit logs for edit_log in self.edit_logs.all(): edit_log.project = published_project edit_log.save() for copyedit_log in self.copyedit_logs.all(): copyedit_log.project = published_project copyedit_log.save() published_project.required_trainings.set( self.required_trainings.all()) # Set files read only and make zip file if requested move_files_as_readonly( published_project.id, self.file_root(), published_project.file_root(), make_zip, verbose_name='Read Only Files - {}'.format( published_project), ) # Remove the ActiveProject self.delete() except BaseException: ProjectFiles().publish_rollback(self, published_project) raise ProjectFiles().publish_complete(self, published_project) return published_project
def save(self): project = super().save(commit=False) slug = get_random_string(20) while exists_project_slug(slug): slug = get_random_string(20) project.slug = slug # Direct copy over fields for field in (field.name for field in Metadata._meta.fields): if field not in ['slug', 'version', 'creation_datetime']: setattr(project, field, getattr(self.latest_project, field)) # Set new fields project.creation_datetime = timezone.now() project.version_order = self.latest_project.version_order + 1 project.is_new_version = True # Change internal links (that point to files within the # published project) to point to their new locations in the # active project project.update_internal_links(old_project=self.latest_project) project.save() # Copy over the author/affiliation objects for p_author in self.latest_project.authors.all(): author = Author.objects.create( project=project, user=p_author.user, display_order=p_author.display_order, is_submitting=p_author.is_submitting, is_corresponding=p_author.is_corresponding, corresponding_email=self.user.get_primary_email(), ) for p_affiliation in p_author.affiliations.all(): Affiliation.objects.create(name=p_affiliation.name, author=author) # Other related objects for p_reference in self.latest_project.references.all(): reference = Reference.objects.create( description=p_reference.description, project=project) for p_publication in self.latest_project.publications.all(): publication = Publication.objects.create( citation=p_publication.citation, url=p_publication.url, project=project) for parent_project in self.latest_project.parent_projects.all(): project.parent_projects.add(parent_project) for p_topic in self.latest_project.topics.all(): Topic.objects.create(project=project, description=p_topic.description) documents = [] content_type = ContentType.objects.get_for_model(ActiveProject) for uploaded_document in self.latest_project.uploaded_documents.all(): uploaded_document.id = None uploaded_document.object_id = project.pk uploaded_document.content_type = content_type uploaded_document.document = ContentFile( content=uploaded_document.document.read(), name=uploaded_document.document.name) documents.append(uploaded_document) UploadedDocument.objects.bulk_create(documents) project.required_trainings.set( self.latest_project.required_trainings.all()) current_file_root = project.file_root() older_file_root = self.latest_project.file_root() ignored_files = ('SHA256SUMS.txt', 'LICENSE.txt') if settings.COPY_FILES_TO_NEW_VERSION: ProjectFiles().cp_dir(older_file_root, current_file_root, ignored_files=ignored_files) return project
def make_zip(self): """ Make a (new) zip file of the main files. """ return ProjectFiles().make_zip(project=self)
def make_checksum_file(self): """ Make the checksums file for the main files """ return ProjectFiles().make_checksum_file(self)
def remove_files(self): """ Remove files of this project """ ProjectFiles().rm_dir(self.file_root(), remove_zip=self.remove_zip) self.set_storage_info()
def clear_files(self): """ Delete the project file directory """ ProjectFiles().rmtree(self.file_root())