def testPluginTemplates(server): val = 'OVERRIDE CORE FOOTER' assert mail_utils.renderTemplate('_footer.mako').strip() == val # Make sure it also works from in-mako import statements content = mail_utils.renderTemplate('temporaryAccess.mako', {'url': 'x'}) assert val in content
def _imagesZipGenerator(self, downloadFileName, images, include): datasetCache = {} zipGenerator = ziputil.ZipGenerator(downloadFileName) for image in images: datasetId = image['meta']['datasetId'] if datasetId not in datasetCache: datasetCache[datasetId] = Dataset().load(datasetId, force=True, exc=True) dataset = datasetCache[datasetId] if include in {'all', 'images'}: imageFile = Image().originalFile(image) imageFileGenerator = File().download(imageFile, headers=False) for data in zipGenerator.addFile(imageFileGenerator, path=os.path.join( dataset['name'], imageFile['name'])): yield data if include in {'all', 'metadata'}: def metadataGenerator(): # TODO: Consider replacing this with Image().filter yield json.dumps({ '_id': str(image['_id']), 'name': image['name'], 'meta': { 'acquisition': image['meta']['acquisition'], 'clinical': image['meta']['clinical'] } }) for data in zipGenerator.addFile( metadataGenerator, path=os.path.join(dataset['name'], '%s.json' % image['name'])): yield data for dataset in six.viewvalues(datasetCache): licenseText = mail_utils.renderTemplate('license_%s.mako' % dataset['license']) attributionText = mail_utils.renderTemplate( 'attribution_%s.mako' % dataset['license'], { 'work': dataset['name'], 'author': dataset['attribution'] }) for data in zipGenerator.addFile(lambda: [licenseText], path=os.path.join( dataset['name'], 'LICENSE.txt')): yield data for data in zipGenerator.addFile(lambda: [attributionText], path=os.path.join( dataset['name'], 'ATTRIBUTION.txt')): yield data yield zipGenerator.footer()
def testPluginTemplates(self): val = 'OVERRIDE CORE FOOTER' self.assertEqual(mail_utils.renderTemplate('_footer.mako').strip(), val) # Make sure it also works from in-mako import statements content = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': 'x' }) self.assertTrue(val in content)
def testPluginTemplates(server): val = 'OVERRIDE CORE FOOTER' assert mail_utils.renderTemplate('_footer.mako').strip() == val # Make sure it also works from in-mako import statements content = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': 'x' }) assert val in content
def testPluginTemplates(self): val = 'OVERRIDE CORE FOOTER' self.assertEqual( mail_utils.renderTemplate('_footer.mako').strip(), val) # Make sure it also works from in-mako import statements content = mail_utils.renderTemplate('temporaryAccess.mako', {'url': 'x'}) self.assertTrue(val in content)
def postScore(self, submission, score, params): # Ensure admin access on the containing challenge phase phase = self.model('phase', 'covalic').load( submission['phaseId'], user=self.getCurrentUser(), exc=True, level=AccessType.ADMIN) # Record whether submission is being re-scored rescoring = 'overallScore' in submission # Save document to trigger computing overall score submission.pop('overallScore', None) submission['score'] = score submission = self.model('submission', 'covalic').save(submission) # Delete the scoring user's job token since the job is now complete. token = self.getCurrentToken() self.model('token').remove(token) user = self.model('user').load(submission['creatorId'], force=True) challenge = self.model('challenge', 'covalic').load( phase['challengeId'], force=True) covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) # Mail user if not rescoring: html = mail_utils.renderTemplate( 'covalic.submissionCompleteUser.mako', { 'phase': phase, 'challenge': challenge, 'submission': submission, 'host': covalicHost }) mail_utils.sendEmail( to=user['email'], subject='Your submission has been scored', text=html) # Mail admins emails = sorted(getPhaseUserEmails( phase, AccessType.WRITE, includeChallengeUsers=True)) html = mail_utils.renderTemplate( 'covalic.submissionCompleteAdmin.mako', { 'user': user, 'phase': phase, 'challenge': challenge, 'submission': submission, 'host': covalicHost }) mail_utils.sendEmail( to=emails, subject='A submission has been scored', text=html) return self._filterScore(phase, submission, user)
def _imagesZipGenerator(self, downloadFileName, images, include): datasetCache = {} zipGenerator = ziputil.ZipGenerator(downloadFileName) for image in images: datasetId = image['meta']['datasetId'] if datasetId not in datasetCache: datasetCache[datasetId] = Dataset().load(datasetId, force=True, exc=True) dataset = datasetCache[datasetId] if include in {'all', 'images'}: imageFile = Image().originalFile(image) imageFileGenerator = File().download(imageFile, headers=False) for data in zipGenerator.addFile( imageFileGenerator, path=os.path.join(dataset['name'], imageFile['name'])): yield data if include in {'all', 'metadata'}: def metadataGenerator(): # TODO: Consider replacing this with Image().filter yield json.dumps({ '_id': str(image['_id']), 'name': image['name'], 'meta': { 'acquisition': image['meta']['acquisition'], 'clinical': image['meta']['clinical'] } }) for data in zipGenerator.addFile( metadataGenerator, path=os.path.join(dataset['name'], '%s.json' % image['name'])): yield data for dataset in datasetCache.values(): licenseText = mail_utils.renderTemplate( 'license_%s.mako' % dataset['license']) attributionText = mail_utils.renderTemplate( 'attribution_%s.mako' % dataset['license'], { 'work': dataset['name'], 'author': dataset['attribution'] }) for data in zipGenerator.addFile( lambda: [licenseText], path=os.path.join(dataset['name'], 'LICENSE.txt')): yield data for data in zipGenerator.addFile( lambda: [attributionText], path=os.path.join(dataset['name'], 'ATTRIBUTION.txt')): yield data yield zipGenerator.footer()
def applyMetadata(self, datasetId, metadataFileId, userId): user = User().load(userId, force=True) dataset = Dataset().load(datasetId, force=True) metadataFileResponse = self.session.get( f'file/{metadataFileId}/download', allow_redirects=False, ) metadataFileResponse.raise_for_status() errors, warnings = Dataset().applyMetadata( dataset=dataset, metadataFileStream=io.BytesIO(metadataFileResponse.content), save=True) # metadata is saved even with warnings status = 'Failed' if errors else 'Succeeded' subject = f'ISIC Archive: Metadata Application {status} - {dataset["name"]}' templateFilename = 'metadataApplication.mako' # Mail user html = mail_utils.renderTemplate( templateFilename, { 'warnings': warnings, 'errors': errors, 'user': user, 'dataset': dataset, }, ) mail_utils.sendMailSync(subject, html, [user['email']] + [u['email'] for u in User().getAdmins()])
def generateTemporaryPassword(self, params): self.requireParams('email', params) email = params['email'].lower().strip() users = self.model('user').find({'email': email}) if not users.count(): raise RestException('That email is not registered.') for user in users: token = self.model('token').createToken(None, days=1, scope=( TokenScope.USER_AUTH, TokenScope.TEMPORARY_USER_AUTH)) token['userId'] = user['_id'] self.model('token').save(token) base = cherrypy.request.base.rstrip('/') altbase = cherrypy.request.headers.get('X-Forwarded-Host', '') if altbase: base = '%s://%s' % (cherrypy.request.scheme, altbase) url = '%s/#useraccount/%s/token/%s' % ( base, str(user['_id']), str(token['_id'])) html = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': url, 'token': str(token['_id']) }) mail_utils.sendEmail(to=email, subject='Girder: Temporary Access', text=html) return {'message': 'Sent temporary access email.'}
def inviteToGroup(self, group, params): """Invite the user to join the group.""" self.requireParams('userId', params) user = self.getCurrentUser() level = int(params.get('level', AccessType.READ)) userToInvite = self.model('user').load(id=params['userId'], user=user, level=AccessType.READ, exc=True) # Can only invite into access levels that you yourself have self.model('group').requireAccess(group, user, level) self.model('group').inviteUser(group, userToInvite, level) if params.get('quiet', '').lower() != 'true': html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="Girder: You've been invited to a group") return self.model('group').filter(group, user, accessList=True, requests=True)
def postScore(self, submission, params): # Ensure admin access on the containing challenge phase phase = self.model('phase', 'challenge').load(submission['phaseId'], user=self.getCurrentUser(), exc=True, level=AccessType.ADMIN) submission['score'] = json.loads(cherrypy.request.body.read()) submission = self.model('submission', 'covalic').save(submission) # Delete the scirubg user's job token since the job is now complete. token = self.getCurrentToken() self.model('token').remove(token) user = self.model('user').load(submission['creatorId'], force=True) challenge = self.model('challenge', 'challenge').load(phase['challengeId'], force=True) covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) html = mail_utils.renderTemplate( 'covalic.submissionComplete.mako', { 'phase': phase, 'challenge': challenge, 'submission': submission, 'host': covalicHost }) mail_utils.sendEmail(to=user['email'], subject='Your submission has been scored', text=html) return submission
def _uploadComplete(event): """ Called after an upload finishes. We check if our current token is a special authorized upload token, and if so, delete it. TODO we could alternatively keep a reference count inside each token that authorized more than a single upload at a time, and just decrement it here. """ token = getCurrentToken() if token and 'authorizedUploadId' in token: user = User().load(token['userId'], force=True) item = Item().load(event.info['file']['itemId'], force=True) # Save the metadata on the item item['description'] = token['authorizedUploadDescription'] item['authorizedUploadEmail'] = token['authorizedUploadEmail'] Item().save(item) text = mail_utils.renderTemplate( 'authorized_upload.uploadFinished.mako', { 'itemId': item['_id'], 'itemName': item['name'], 'itemDescription': item.get('description', '') }) mail_utils.sendEmail(to=user['email'], subject='Authorized upload complete', text=text) Token().remove(token)
def sendIngestionNotification(batchId, failedImages, skippedFilenames): batch = Batch().load(batchId) dataset = Dataset().load(batch['datasetId'], force=True) user = User().load(batch['creatorId'], force=True) host = mail_utils.getEmailUrlPrefix() # TODO: The email should gracefully handle the situation where failedImages or skippedFilenames # has an excessive amount of items. params = { 'isOriginalUploader': True, 'host': host, 'dataset': dataset, # We intentionally leak full user details here, even though all # email recipients may not have access permissions to the user 'user': user, 'batch': batch, 'failedImages': failedImages, 'skippedFilenames': skippedFilenames } subject = 'ISIC Archive: Dataset Upload Confirmation' templateFilename = 'ingestDatasetConfirmation.mako' # Mail user html = mail_utils.renderTemplate(templateFilename, params) sendEmail(to=user['email'], subject=subject, text=html) # Mail 'Dataset QC Reviewers' group params['isOriginalUploader'] = False sendEmailToGroup( groupName='Dataset QC Reviewers', templateFilename=templateFilename, templateParams=params, subject=subject)
def postScore(self, submission, params): # Ensure admin access on the containing challenge phase phase = self.model('phase', 'challenge').load( submission['phaseId'], user=self.getCurrentUser(), exc=True, level=AccessType.ADMIN) submission['score'] = json.loads(cherrypy.request.body.read()) submission = self.model('submission', 'covalic').save(submission) # Delete the scirubg user's job token since the job is now complete. token = self.getCurrentToken() self.model('token').remove(token) user = self.model('user').load(submission['creatorId'], force=True) challenge = self.model('challenge', 'challenge').load( phase['challengeId'], force=True) covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) html = mail_utils.renderTemplate('covalic.submissionComplete.mako', { 'phase': phase, 'challenge': challenge, 'submission': submission, 'host': covalicHost }) mail_utils.sendEmail( to=user['email'], subject='Your submission has been scored', text=html) return submission
def sendIngestionNotification(batchId, failedImages, skippedFilenames, numImages): batch = Batch().load(batchId) dataset = Dataset().load(batch['datasetId'], force=True) user = User().load(batch['creatorId'], force=True) host = mail_utils.getEmailUrlPrefix() # TODO: The email should gracefully handle the situation where failedImages or skippedFilenames # has an excessive amount of items. params = { 'isOriginalUploader': True, 'host': host, 'dataset': dataset, # We intentionally leak full user details here, even though all # email recipients may not have access permissions to the user 'user': user, 'batch': batch, 'failedImages': failedImages, 'skippedFilenames': skippedFilenames, 'numImages': numImages, } subject = f'ISIC Archive: Dataset Upload Confirmation - {dataset["name"]}' templateFilename = 'ingestDatasetConfirmation.mako' # Mail user html = mail_utils.renderTemplate(templateFilename, params) mail_utils.sendMailSync(subject, html, user['email']) # Mail 'Dataset QC Reviewers' group params['isOriginalUploader'] = False sendEmailToGroup( groupName='Dataset QC Reviewers', templateFilename=templateFilename, templateParams=params, subject=subject, asynchronous=False)
def _uploadComplete(event): """ Called after an upload finishes. We check if our current token is a special authorized upload token, and if so, delete it. TODO we could alternatively keep a reference count inside each token that authorized more than a single upload at a time, and just decrement it here. """ token = getCurrentToken() if 'authorizedUploadId' in token: user = ModelImporter.model('user').load(token['userId'], force=True) item = ModelImporter.model('item').load(event.info['file']['itemId'], force=True) # Save the metadata on the item item['description'] = token['authorizedUploadDescription'] item['authorizedUploadEmail'] = token['authorizedUploadEmail'] ModelImporter.model('item').save(item) text = mail_utils.renderTemplate('authorized_upload.uploadFinished.mako', { 'itemId': item['_id'], 'itemName': item['name'], 'itemDescription': item.get('description', '') }) mail_utils.sendEmail(to=user['email'], subject='Authorized upload complete', text=text) ModelImporter.model('token').remove(token)
def _sendApprovalEmail(self, user): url = '%s#user/%s' % (mail_utils.getEmailUrlPrefix(), str(user['_id'])) text = mail_utils.renderTemplate('accountApproval.mako', { 'user': user, 'url': url }) mail_utils.sendMailToAdmins('Girder: Account pending approval', text)
def inviteToGroup(self, group, params): """Invite the user to join the group.""" self.requireParams(('userId',), params) user = self.getCurrentUser() level = int(params.get('level', AccessType.READ)) userToInvite = self.model('user').load( id=params['userId'], user=user, level=AccessType.READ, exc=True) # Can only invite into access levels that you yourself have self.model('group').requireAccess(group, user, level) self.model('group').inviteUser(group, userToInvite, level) if params.get('quiet', '').lower() != 'true': html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="Girder: You've been invited to a group") return self.model('group').filter(group, user, accessList=True, requests=True)
def _sendApprovedEmail(self, user): text = mail_utils.renderTemplate('accountApproved.mako', { 'user': user, 'url': mail_utils.getEmailUrlPrefix() }) mail_utils.sendMail('Girder: Account approved', text, [user.get('email')])
def generateTemporaryPassword(self, params): self.requireParams('email', params) email = params['email'].lower().strip() users = self.model('user').find({'email': email}) if not users.count(): raise RestException('That email is not registered.') for user in users: token = self.model('token').createToken( None, days=1, scope=(TokenScope.USER_AUTH, TokenScope.TEMPORARY_USER_AUTH)) token['userId'] = user['_id'] self.model('token').save(token) base = cherrypy.request.base.rstrip('/') altbase = cherrypy.request.headers.get('X-Forwarded-Host', '') if altbase: base = '%s://%s' % (cherrypy.request.scheme, altbase) url = '%s/#useraccount/%s/token/%s' % (base, str( user['_id']), str(token['_id'])) html = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': url, 'token': str(token['_id']) }) mail_utils.sendEmail(to=email, subject='Girder: Temporary Access', text=html) return {'message': 'Sent temporary access email.'}
def send_new_user_email(event): try: info = event.info email = info.get('email') brandName = Setting().get(SettingKey.BRAND_NAME) rendered = renderTemplate('welcome.mako') sendMail(f'Welcome to {brandName}', rendered, [email]) except Exception: logger.exception("Failed to send new user email")
def _sendApprovedEmail(self, user): text = mail_utils.renderTemplate('accountApproved.mako', { 'user': user, 'url': mail_utils.getEmailUrlPrefix() }) mail_utils.sendEmail( to=user.get('email'), subject='Girder: Account approved', text=text)
def _sendVerificationEmail(self, user): token = self.model('token').createToken( user, days=1, scope=TokenScope.EMAIL_VERIFICATION) url = '%s#useraccount/%s/verification/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str( token['_id'])) text = mail_utils.renderTemplate('emailVerification.mako', {'url': url}) mail_utils.sendEmail(to=user.get('email'), subject='Girder: Email verification', text=text)
def _sendApprovalEmail(self, user): url = '%s/#user/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id'])) text = mail_utils.renderTemplate('accountApproval.mako', { 'user': user, 'url': url }) mail_utils.sendEmail( toAdmins=True, subject='Girder: Account pending approval', text=text)
def _sendVerificationEmail(self, user): token = self.model('token').createToken( user, days=1, scope=TokenScope.EMAIL_VERIFICATION) url = '%s/#useraccount/%s/verification/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str(token['_id'])) text = mail_utils.renderTemplate('emailVerification.mako', { 'url': url }) mail_utils.sendEmail( to=user.get('email'), subject='Girder: Email verification', text=text)
def inviteToGroup(self, group, params): """Invite the user to join the group.""" self.requireParams('userId', params) user = self.getCurrentUser() level = int(params.get('level', AccessType.READ)) force = self.boolParam('force', params, default=False) userToInvite = self.model('user').load(id=params['userId'], user=user, level=AccessType.READ, exc=True) if force: if not user.get('admin', False): mustBeAdmin = True addPolicy = self.model('setting').get( SettingKey.ADD_TO_GROUP_POLICY) addGroup = group.get('addAllowed', 'default') if addGroup not in ['no', 'yesadmin', 'yesmod']: addGroup = addPolicy if (self.model('group').hasAccess(group, user, AccessType.ADMIN) and ('mod' in addPolicy or 'admin' in addPolicy) and addGroup.startswith('yes')): mustBeAdmin = False elif (self.model('group').hasAccess( group, user, AccessType.WRITE) and 'mod' in addPolicy and addGroup == 'yesmod'): mustBeAdmin = False if mustBeAdmin: self.requireAdmin(user) self.model('group').addUser(group, userToInvite, level=level) else: # Can only invite into access levels that you yourself have self.model('group').requireAccess(group, user, level) self.model('group').inviteUser(group, userToInvite, level) if not self.boolParam('quiet', params, default=False): html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="Girder: You've been invited to a group") return self.model('group').filter(group, user, accessList=True, requests=True)
def send_approved(event): dataset = event.info['dataset'] approver = event.info['approver'] user = User().load(dataset['userId'], force=True) html = mail_utils.renderTemplate('mdb.dataset_approved.mako', { 'dataset': dataset, 'user': user, 'approver': approver }) email_address = user['email'] mail_utils.sendEmail(to=email_address, subject='Materials Data Bank: Dataset approved.', text=html)
def _sendVerificationEmail(self, user): from .token import Token token = Token().createToken(user, days=1, scope=TokenScope.EMAIL_VERIFICATION) url = '%s#useraccount/%s/verification/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str( token['_id'])) text = mail_utils.renderTemplate('emailVerification.mako', {'url': url}) mail_utils.sendMail('Girder: Email verification', text, [user.get('email')])
def resetPassword(self, email): user = self.model('user').findOne({'email': email.lower()}) if user is None: raise RestException('That email is not registered.') randomPass = genToken(length=12) html = mail_utils.renderTemplate('resetPassword.mako', { 'password': randomPass }) mail_utils.sendEmail(to=email, subject='Girder: Password reset', text=html) self.model('user').setPassword(user, randomPass) return {'message': 'Sent password reset email.'}
def send_created(event): dataset = event.info['dataset'] print(dataset) user = event.info['user'] html = mail_utils.renderTemplate('mdb.dataset_created.mako', { 'dataset': dataset, 'user': user }) email_address = Setting().get(constants.NOTIFICATION_EMAIL) mail_utils.sendEmail(to=email_address, subject='Materials Data Bank: Dataset submitted.', text=html)
def resetPassword(self, email, params): user = self.model('user').findOne({'email': email.lower()}) if user is None: raise RestException('That email is not registered.') randomPass = genToken(length=12) html = mail_utils.renderTemplate('resetPassword.mako', { 'password': randomPass }) mail_utils.sendEmail(to=email, subject='Girder: Password reset', text=html) self.model('user').setPassword(user, randomPass) return {'message': 'Sent password reset email.'}
def inviteToGroup(self, group, params): self.requireParams('userId', params) user = self.getCurrentUser() level = int(params.get('level', AccessType.READ)) force = self.boolParam('force', params, default=False) groupModel = self.model('group') userToInvite = self.model('user').load( id=params['userId'], user=user, level=AccessType.READ, exc=True) if force: if not user.get('admin', False): mustBeAdmin = True addPolicy = self.model('setting').get( SettingKey.ADD_TO_GROUP_POLICY) addGroup = group.get('addAllowed', 'default') if addGroup not in ['no', 'yesadmin', 'yesmod']: addGroup = addPolicy if (groupModel.hasAccess( group, user, AccessType.ADMIN) and ('mod' in addPolicy or 'admin' in addPolicy) and addGroup.startswith('yes')): mustBeAdmin = False elif (groupModel.hasAccess( group, user, AccessType.WRITE) and 'mod' in addPolicy and addGroup == 'yesmod'): mustBeAdmin = False if mustBeAdmin: self.requireAdmin(user) groupModel.addUser(group, userToInvite, level=level) else: # Can only invite into access levels that you yourself have groupModel.requireAccess(group, user, level) groupModel.inviteUser(group, userToInvite, level) if not self.boolParam('quiet', params, default=False): html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="Girder: You've been invited to a group") group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group
def _sendMail(self, folder, userId, subject, template): """ Sends the specified email template to a single user. :param folder: the curated folder :param userId: the id of the user to email :param subject: the email subject :param template: the name of the mako template to use """ if not userId: return data = dict(folder=folder, curation=folder[CURATION]) text = mail_utils.renderTemplate(template, data) emails = [self._getEmail(userId)] mail_utils.sendEmail(emails, subject, text)
def onJobUpdate(event): """ Hook into job update event so we can look for job failure events and email administrators accordingly. """ if (event.info['job']['type'] == 'covalic_score' and 'status' in event.info['params'] and int(event.info['params']['status']) == JobStatus.ERROR): covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) html = mail_utils.renderTemplate('covalic.submissionError.mako', { 'submissionId': event.info['job']['covalicSubmissionId'], 'host': covalicHost }) mail_utils.sendEmail( toAdmins=True, subject='Submission processing error', text=html)
def inviteUser(self, params): Token = self.model('token') User = self.model('user', 'isic_archive') params = self._decodeParams(params) self.requireParams(['login', 'email', 'firstName', 'lastName'], params) if 'validityPeriod' in params: try: validityPeriod = float(params['validityPeriod']) except ValueError: raise ValidationException('Validity period must be a number.', 'validityPeriod') else: validityPeriod = 60.0 currentUser = self.getCurrentUser() User.requireAdminStudy(currentUser) newUser = User.createUser( login=params['login'], password=None, email=params['email'], firstName=params['firstName'], lastName=params['lastName'] ) token = Token.createToken( newUser, days=validityPeriod, scope=[TokenScope.TEMPORARY_USER_AUTH]) inviteUrl = '%s/#user/%s/rsvp/%s' % ( mail_utils.getEmailUrlPrefix(), newUser['_id'], token['_id']) html = mail_utils.renderTemplate( 'inviteUser.mako', { 'newUser': newUser, 'inviteUrl': inviteUrl, }) mail_utils.sendEmail( to=newUser['email'], subject='ISIC Archive: Invitation', text=html) return { 'newUser': User.filterSummary(newUser, currentUser), 'inviteUrl': inviteUrl }
def onJobUpdate(event): """ Hook into job update event so we can look for job failure events and email administrators accordingly. """ if (event.info['job']['type'] == 'covalic_score' and 'status' in event.info['params'] and int(event.info['params']['status']) == JobStatus.ERROR): covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) html = mail_utils.renderTemplate( 'covalic.submissionError.mako', { 'submissionId': event.info['job']['covalicSubmissionId'], 'host': covalicHost }) mail_utils.sendEmail(toAdmins=True, subject='Submission processing error', text=html)
def requestCreateDatasetPermission(self, params): User = self.model('user', 'isic_archive') Group = self.model('group') currentUser = self.getCurrentUser() resp = {} if User.canCreateDataset(currentUser): resp['message'] = 'Dataset Contributor access granted.', resp['extra'] = 'hasPermission' else: # Request that user join group groupName = 'Dataset Contributors' group = Group.findOne({'name': groupName}) if not group: raise RestException('Could not load group: %s' % groupName) resp['message'] = 'Dataset Contributor access requested. An administrator may contact ' \ 'you via email (at %s) to process your request.' % currentUser['email'] for request in Group.getFullRequestList(group): if request['id'] == currentUser['_id']: # Request for this user is already pending break else: # No request for this user yet Group.joinGroup(group, currentUser) # Send email to group moderators and administrators groupAcl = Group.getFullAccessList(group) groupModeratorEmails = [ getUserEmail(user) for user in groupAcl['users'] if user['level'] >= AccessType.WRITE ] host = mail_utils.getEmailUrlPrefix() html = mail_utils.renderTemplate( 'datasetContributorRequest.mako', { 'user': currentUser, 'group': group, 'host': host, }) mail_utils.sendEmail( to=groupModeratorEmails, subject='ISIC Archive: Dataset Contributor Request', text=html) return resp
def sendEmailToGroup(groupName, templateFilename, templateParams, subject=None): """ Send a single email with all members of a group as the recipients. :param groupName: The name of the group. :param templateFilename: The name of the Make template file used to format the email. :param templateParams: The parameters with which to render the template. :param subject: The subject line of the email. """ group = Group().findOne({'name': groupName}) if not group: raise Exception('Could not load group: %s.' % groupName) emails = [member['email'] for member in Group().listMembers(group)] if emails: html = mail_utils.renderTemplate(templateFilename, templateParams) sendEmail(to=emails, subject=subject, text=html)
def _sendMail(self, folder, userId, subject, template): """ Sends the specified email template to a single user. :param folder: the curated folder :param userId: the id of the user to email :param subject: the email subject :param template: the name of the mako template to use """ if not userId: return data = dict( folder=folder, curation=folder[CURATION]) text = mail_utils.renderTemplate(template, data) emails = [self._getEmail(userId)] mail_utils.sendEmail(emails, subject, text)
def generateTemporaryPassword(self, email): user = self.model('user').findOne({'email': email.lower()}) if not user: raise RestException('That email is not registered.') token = self.model('token').createToken( user, days=1, scope=TokenScope.TEMPORARY_USER_AUTH) url = '%s#useraccount/%s/token/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str(token['_id'])) html = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': url, 'token': str(token['_id']) }) mail_utils.sendEmail(to=email, subject='Girder: Temporary access', text=html) return {'message': 'Sent temporary access email.'}
def resetPassword(self, params): self.requireParams('email', params) email = params['email'].lower().strip() cursor = self.model('user').find({'email': email}, limit=1) if cursor.count() == 0: raise RestException('That email is not registered.') user = cursor.next() randomPass = genToken(length=12) html = mail_utils.renderTemplate('resetPassword.mako', {'password': randomPass}) mail_utils.sendEmail(to=email, subject='Girder: Password reset', text=html) self.model('user').setPassword(user, randomPass) return {'message': 'Sent password reset email.'}
def resetPassword(self, params): self.requireParams(('email',), params) email = params['email'].lower().strip() cursor = self.model('user').find({'email': email}, limit=1) if cursor.count() == 0: raise RestException('That email is not registered.') user = cursor.next() randomPass = genToken(length=12) html = mail_utils.renderTemplate('resetPassword.mako', { 'password': randomPass }) mail_utils.sendEmail(to=email, subject='Girder: Password reset', text=html) self.model('user').setPassword(user, randomPass) return {'message': 'Sent password reset email.'}
def generateTemporaryPassword(self, email, params): user = self.model('user').findOne({'email': email.lower()}) if not user: raise RestException('That email is not registered.') token = self.model('token').createToken( user, days=1, scope=TokenScope.TEMPORARY_USER_AUTH) url = '%s#useraccount/%s/token/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str(token['_id'])) html = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': url, 'token': str(token['_id']) }) mail_utils.sendEmail(to=email, subject='Girder: Temporary access', text=html) return {'message': 'Sent temporary access email.'}
def inviteToGroup(self, group, userToInvite, level, quiet, force): groupModel = self._model user = self.getCurrentUser() if force: if not user['admin']: mustBeAdmin = True addPolicy = Setting().get(SettingKey.ADD_TO_GROUP_POLICY) addGroup = group.get('addAllowed', 'default') if addGroup not in ['no', 'yesadmin', 'yesmod']: addGroup = addPolicy if (groupModel.hasAccess( group, user, AccessType.ADMIN) and ('mod' in addPolicy or 'admin' in addPolicy) and addGroup.startswith('yes')): mustBeAdmin = False elif (groupModel.hasAccess( group, user, AccessType.WRITE) and 'mod' in addPolicy and addGroup == 'yesmod'): mustBeAdmin = False if mustBeAdmin: self.requireAdmin(user) groupModel.addUser(group, userToInvite, level=level) else: # Can only invite into access levels that you yourself have groupModel.requireAccess(group, user, level) groupModel.inviteUser(group, userToInvite, level) if not quiet: html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="%s: You've been invited to a group" % Setting().get(SettingKey.BRAND_NAME) ) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group
def _ingestZip(self, dataset, zipFile, batch, user, sendMail): """ Ingest images from a ZIP file into a dataset. The images are extracted to a "Pre-review" folder within the dataset folder. """ prereviewFolder = Folder().createFolder( parent=self.imagesFolder(dataset), name='Pre-review', parentType='folder', creator=user, public=False, reuseExisting=True) # Process zip file # TODO: gracefully clean up after exceptions in handleZip self._handleZip(dataset, batch, prereviewFolder, user, zipFile) # Send email confirmations if sendMail: host = mail_utils.getEmailUrlPrefix() params = { 'group': False, 'host': host, 'dataset': dataset, # We intentionally leak full user details here, even though all # email recipients may not have access permissions to the user 'user': user, 'batch': batch, } subject = 'ISIC Archive: Dataset Upload Confirmation' templateFilename = 'ingestDatasetConfirmation.mako' # Mail user html = mail_utils.renderTemplate(templateFilename, params) mail_utils.sendEmail(to=user['email'], subject=subject, text=html) # Mail 'Dataset QC Reviewers' group params['group'] = True isic_mail_utils.sendEmailToGroup( groupName='Dataset QC Reviewers', templateFilename=templateFilename, templateParams=params, subject=subject)
def sendEmailToGroup(groupName, templateFilename, templateParams, subject=None): """ Send a single email with all members of a group as the recipients. :param groupName: The name of the group. :param templateFilename: The name of the Make template file used to format the email. :param templateParams: The parameters with which to render the template. :param subject: The subject line of the email. """ group = Group().findOne({'name': groupName}) if not group: raise Exception('Could not load group: %s.' % groupName) emails = [member['email'] for member in Group().listMembers(group)] if emails: html = mail_utils.renderTemplate(templateFilename, templateParams) mail_utils.sendEmail(to=emails, subject=subject, text=html)
def requestCreateDatasetPermission(self, params): currentUser = self.getCurrentUser() resp = {} if User().canCreateDataset(currentUser): resp['message'] = 'Dataset Contributor access granted.', resp['extra'] = 'hasPermission' else: # Request that user join group groupName = 'Dataset Contributors' group = Group().findOne({'name': groupName}) if not group: raise RestException('Could not load group: %s' % groupName) resp['message'] = 'Dataset Contributor access requested. An administrator may contact ' \ 'you via email (at %s) to process your request.' % currentUser['email'] for request in Group().getFullRequestList(group): if request['id'] == currentUser['_id']: # Request for this user is already pending break else: # No request for this user yet Group().joinGroup(group, currentUser) # Send email to group moderators and administrators groupAcl = Group().getFullAccessList(group) groupModeratorEmails = [ getUserEmail(user) for user in groupAcl['users'] if user['level'] >= AccessType.WRITE ] if groupModeratorEmails: host = mail_utils.getEmailUrlPrefix() html = mail_utils.renderTemplate( 'datasetContributorRequest.mako', { 'user': currentUser, 'group': group, 'host': host, }) mail_utils.sendEmail( to=groupModeratorEmails, subject='ISIC Archive: Dataset Contributor Request', text=html) return resp
def generateTemporaryPassword(self, email): user = self._model.findOne({'email': email.lower()}) if not user: raise RestException('That email is not registered.') token = Token().createToken(user, days=1, scope=TokenScope.TEMPORARY_USER_AUTH) url = '%s#useraccount/%s/token/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str(token['_id'])) html = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': url, 'token': str(token['_id']) }) mail_utils.sendMail( '%s: Temporary access' % Setting().get(SettingKey.BRAND_NAME), html, [email] ) return {'message': 'Sent temporary access email.'}
def sendEmailToGroup(groupName, templateFilename, templateParams, subject=None, asynchronous=True): """ Send a single email with all members of a group as the recipients. :param groupName: The name of the group. :param templateFilename: The name of the Make template file used to format the email. :param templateParams: The parameters with which to render the template. :param subject: The subject line of the email. :param asynchronous: If False, bypass Girder's event system. """ group = Group().findOne({'name': groupName}) if not group: raise Exception(f'Could not load group: {groupName}.') emails = [member['email'] for member in Group().listMembers(group)] if emails: html = mail_utils.renderTemplate(templateFilename, templateParams) if asynchronous: mail_utils.sendMail(subject, html, emails) else: mail_utils.sendMailSync(subject, html, emails)
def inviteUser(self, params): params = self._decodeParams(params) self.requireParams(['login', 'email', 'firstName', 'lastName'], params) if 'validityPeriod' in params: try: validityPeriod = float(params['validityPeriod']) except ValueError: raise ValidationException('Validity period must be a number.', 'validityPeriod') else: validityPeriod = 60.0 currentUser = self.getCurrentUser() User().requireAdminStudy(currentUser) newUser = User().createUser(login=params['login'], password=None, email=params['email'], firstName=params['firstName'], lastName=params['lastName']) token = Token().createToken(newUser, days=validityPeriod, scope=[TokenScope.TEMPORARY_USER_AUTH]) inviteUrl = '%s/#user/%s/rsvp/%s' % (mail_utils.getEmailUrlPrefix(), newUser['_id'], token['_id']) html = mail_utils.renderTemplate('inviteUser.mako', { 'newUser': newUser, 'inviteUrl': inviteUrl, }) mail_utils.sendEmail(to=newUser['email'], subject='ISIC Archive: Invitation', text=html) return { 'newUser': User().filterSummary(newUser, currentUser), 'inviteUrl': inviteUrl }
def ingestDataset(self, zipFile, user, name, owner, description, license, signature, anonymous, attribution, sendMail=False): """ Ingest an uploaded dataset from a .zip file of images. The images are extracted to a "Pre-review" folder within a new dataset folder. """ Folder = self.model('folder') # Create dataset folder dataset = self.createDataset(name, description, user) # Set dataset metadata, including license info dataset = self.setMetadata(dataset, { 'owner': owner, 'signature': signature, 'anonymous': anonymous, 'attribution': attribution, 'license': license, 'metadataFiles': [] }) prereviewFolder = Folder.createFolder( parent=dataset, name='Pre-review', parentType='folder', creator=user, public=False) prereviewFolder = Folder.copyAccessPolicies( dataset, prereviewFolder, save=True) # Process zip file # TODO: gracefully clean up after exceptions in handleZip self._handleZip(prereviewFolder, user, zipFile) # Send email confirmations if sendMail: host = mail_utils.getEmailUrlPrefix() params = { 'group': False, 'host': host, # We intentionally leak full user details here, even though all # email recipients may not have access permissions to the user 'user': user, 'name': name, 'owner': owner, 'description': description, 'license': license, 'signature': signature, 'attribution': 'Anonymous' if anonymous else attribution } subject = 'ISIC Archive: Dataset Upload Confirmation' templateFilename = 'ingestDatasetConfirmation.mako' # Mail user html = mail_utils.renderTemplate(templateFilename, params) mail_utils.sendEmail(to=user['email'], subject=subject, text=html) # Mail 'Dataset QC Reviewers' group params['group'] = True isic_mail_utils.sendEmailToGroup( groupName='Dataset QC Reviewers', templateFilename=templateFilename, templateParams=params, subject=subject) return dataset
def onJobUpdate(event): """ Hook into job update event so we can look for job failure events and email the user and challenge/phase administrators accordingly. Here, an administrator is defined to be a user with WRITE access or above. """ isErrorStatus = False try: isErrorStatus = int(event.info['params'].get('status')) == JobStatus.ERROR except (ValueError, TypeError): pass if (event.info['job']['type'] == 'covalic_score' and isErrorStatus): covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) # Create minimal log that contains only Covalic errors. # Use full log if no Covalic-specific errors are found. # Fetch log from model, because log in event may not be up-to-date. job = Job().load( event.info['job']['_id'], includeLog=True, force=True) log = job.get('log') minimalLog = None if log: log = ''.join(log) minimalLog = '\n'.join([line[len(JOB_LOG_PREFIX):].strip() for line in log.splitlines() if line.startswith(JOB_LOG_PREFIX)]) if not minimalLog: minimalLog = log submission = Submission().load( event.info['job']['covalicSubmissionId']) phase = Phase().load( submission['phaseId'], force=True) challenge = Challenge().load( phase['challengeId'], force=True) user = User().load( event.info['job']['userId'], force=True) rescoring = job.get('rescoring', False) # Mail admins, include full log emails = sorted(getPhaseUserEmails( phase, AccessType.WRITE, includeChallengeUsers=True)) html = mail_utils.renderTemplate('covalic.submissionErrorAdmin.mako', { 'submission': submission, 'challenge': challenge, 'phase': phase, 'user': user, 'host': covalicHost, 'log': log }) mail_utils.sendEmail( to=emails, subject='Submission processing error', text=html) # Mail user, include minimal log if not rescoring: html = mail_utils.renderTemplate('covalic.submissionErrorUser.mako', { 'submission': submission, 'challenge': challenge, 'phase': phase, 'host': covalicHost, 'log': minimalLog }) mail_utils.sendEmail( to=user['email'], subject='Submission processing error', text=html)