def test_build_raises_exception_if_parts_class_does_not_exist( self, mock_config, mock_parser): mock_config.return_value = [self._path] with self.assertRaises(InvalidClassError): Configuration('invalid_report_parts.json') document = DocumentController() document.build()
class Annotations(object): """ Contains the structure for generating release notes by collating the issue ID, release note text and business representative for all issues assigned to the current fix version which are in state done but not in state rejected. """ CONFIGURATION = 'bio.json' _document_controller = None _document_ready = False _document_title = 'Sequence Annotation' def __init__(self): """ initialise the ReleaseNote object """ self._document_controller = DocumentController(self.CONFIGURATION) def build_document(self): """ Creates the document structure """ try: self._document_controller.build() self._document_ready = True except: self._document_ready = False raise def publish(self): """ publish the document """ if not self._document_ready: self.build_document() self._document_controller.save(self._document_title + ' ' + '.docx')
def test_render(self, mock_config, mock_parser): mock_config.return_value = [self._path] Configuration('config_simple.json') document = DocumentController() self.assertIsInstance(document.configuration, Configuration) self.assertIsInstance(document.threadmanager, ThreadManager) self.assertIsInstance(document.reportmanager, ReportManager) self.assertIsInstance(document.partfactory, DocumentPartFactory) with patch('docx.document.Document.add_heading') as mock_heading, \ patch('docx.document.Document.add_paragraph') as mock_paragraph, \ patch('docx.document.Document.save') as mock_save: document.build() heading_calls = [ call('Test document structure for WeeklyReport/Helicopter view', 0), call('Week 41', 1), call('hello world', 1), #call('Test title', 3), call('This has sub-sections', 1), call('this is sub section 1', 2), call('this is sub section 2', 2), call('This section uses a file path for its text', 1) ] self.assertEquals(mock_heading.call_count, len(heading_calls)) mock_heading.assert_has_calls(heading_calls) self.assertEquals(17, mock_paragraph.call_count)
def test_document_raises_thread_failed_error_if_threadmanager_execute_returns_false( self, mock_config, mock_parser): mock_config.return_value = [self._path] Configuration('config_simple.json') document = DocumentController() with patch('pyccata.core.managers.thread.ThreadManager.execute' ) as mock_thread: mock_thread.return_value = False with self.assertRaises(ThreadFailedError): document.build()
def test_csv_with_no_file(self, mock_config_locations, mock_parse): self.tearDown() mock_config_locations.return_value = [self._path] with patch('pyccata.core.managers.clients.docx.Docx') as docx: docx.__implements__ = (ReportingInterface, ) document = DocumentController('csv_no_file.json') document.build() csvfiles = document._thread_manager.projectmanager._client._client self.assertIsInstance(csvfiles, CSVClient) self.assertEquals(len(csvfiles), 0)
def test_report_manager_loads_document_part_factory( self, mock_config, mock_parser, mock_format): mock_config.return_value = [self._path] Configuration('config_sections.json') document = DocumentController() self.assertIsInstance(document.configuration, Configuration) self.assertIsInstance(document.threadmanager, ThreadManager) self.assertIsInstance(document.reportmanager, ReportManager) self.assertIsInstance(document.partfactory, DocumentPartFactory) document.format_for_email() self.assertEquals(1, mock_format.call_count)
def test_add_callback_hands_off_to_report_manager(self, mock_config, mock_parser, mock_callback): mock_config.return_value = [self._path] Configuration('config_sections.json') document = DocumentController() self.assertIsInstance(document.configuration, Configuration) self.assertIsInstance(document.threadmanager, ThreadManager) self.assertIsInstance(document.reportmanager, ReportManager) self.assertIsInstance(document.partfactory, DocumentPartFactory) document.add_callback('test', 'test_method') self.assertEquals(1, mock_callback.call_count) mock_callback.assert_called_with('test', 'test_method')
def test_thread_manager_raises_exception(self, exception, message, mock_config, mock_manager_load, mock_parser): mock_manager_load.side_effect = exception(message) mock_config.return_value = [self._path] Configuration('config_sections.json') with self.assertRaises(exception): DocumentController()
def test_threadmanager_raises_exception_during_initialisation( self, mock_threadmanager, mock_config, mock_parser): mock_config.return_value = [self._path] Configuration('config_sections.json') mock_threadmanager.side_effect = AttributeError( 'Invalid attribute \'foo\' for manager ThreadManager') with self.assertRaises(AttributeError): DocumentController()
def test_report_manager_raises_invalid_module_error_if_importlib_fails( self, mock_import, mock_config, mock_thread, mock_report): mock_report.return_value = None mock_thread.return_value = None mock_config.return_value = None mock_import.side_effect = ImportError('Failed to load module') with self.assertRaises(InvalidModuleError): DocumentController()
def test_report_manager_raises_invalid_module_error_if_parts_module_doesnt_exist( self, mock_config, mock_parser): mock_config.return_value = [self._path] Configuration('config_sections.json') with self.assertRaises(InvalidModuleError): with patch('pyccata.core.factory.DocumentPartFactory.MODULE', new_callable=PropertyMock) as mock_module: mock_module.return_value = 'pts' DocumentController()
def test_csv_with_broken_file(self, mock_dataframe, mock_config_locations, mock_parse): self.tearDown() mock_config_locations.return_value = [self._path] mock_dataframe.side_effect = DataProviders.get_csv_results() with patch('pyccata.core.managers.clients.docx.Docx') as docx: docx.__implements__ = (ReportingInterface, ) document = DocumentController('broken_csv.json') document.build() csvfiles = document._thread_manager.projectmanager._client._client self.assertIsInstance(csvfiles, CSVClient) self.assertEquals(len(csvfiles), 0) document.save('Test Document.docx')
def test_csv_with_multi_file(self, mock_dataframe, mock_config_locations, mock_parse): mock_config_locations.return_value = [self._path] mock_dataframe.side_effect = DataProviders.get_csv_results() with patch('pyccata.core.managers.clients.docx.Docx') as docx: docx.__implements__ = (ReportingInterface, ) document = DocumentController('csv_multi_file.json') document.build() self.assertEquals( None, document._thread_manager.projectmanager._client.server) self.assertIsInstance( document._thread_manager.projectmanager._client.projects(), list) csvfiles = document._thread_manager.projectmanager._client._client self.assertIsInstance(csvfiles, CSVClient) self.assertEquals(len(csvfiles), 2) for csv in csvfiles: self.assertIsInstance(csv._dataframe, pandas.DataFrame) self.assertEquals(len(csv._dataframe.index), 3) document.save('Test Document.docx')
class ReleaseNote(object): """ Contains the structure for generating release notes by collating the issue ID, release note text and business representative for all issues assigned to the current fix version which are in state done but not in state rejected. """ CONFIGURATION = 'releasenote.json' _document_controller = None _document_ready = False _document_title = 'MSS Platform Release' def __init__(self): """ initialise the ReleaseNote object """ self._document_controller = DocumentController(self.CONFIGURATION) def build_document(self): """ Creates the document structure """ try: self._document_controller.build() self._document_ready = True except: self._document_ready = False raise def publish(self): """ publish the document """ if not self._document_ready: self.build_document() date = datetime.strftime(datetime.now(), '%Y-%m-%d') self._document_controller.format_for_email() self._document_controller.save(self._document_title + ' ' + date + '.docx')
def __init__(self): """ initialise the ReleaseNote object """ self._document_controller = DocumentController(self.CONFIGURATION)
def test_configuration_raises_exception(self, exception, message, mock_config): mock_config.side_effect = exception(*message) with self.assertRaises(exception): DocumentController()
class ReleaseInstructions(object): """ Contains the structure for generating release notes by collating the issue ID, release note text and business representative for all issues assigned to the current fix version which are in state done but not in state rejected. """ CONFIGURATION = 'releaseinstructions.json' _document_controller = None _document_ready = False _document_title = 'MSS Platform Release Rollout and Rollback' _configuration = None _regex = re.compile( '^([0-9]*)?_?(\w+-\d+)_([a-zA-Z-]+)_(\w+)_(UP|DOWN)_([0-9]*)?_?(\w+)(.[a-zA-Z]+)?$', re.IGNORECASE ) def __init__(self): """ initialise the ReleaseNote object """ self._document_controller = DocumentController(self.CONFIGURATION) self._configuration = self._document_controller.configuration self._document_controller.add_callback('attachments', getattr(ReleaseInstructions, 'file_collator')) def build_document(self): """ Creates the document structure """ try: self._document_controller.build() final = Replacements().find('FINAL').value if final in ['1', 'True', 'TRUE', 'true']: pipelines = self._document_controller.threadmanager.find('Deployment Pipelines') if pipelines is not None or len(pipelines) > 0: for pipeline in pipelines: self.pipeline_trigger(pipeline) self._document_ready = True except: self._document_ready = False raise def publish(self): """ publish the document """ if not self._document_ready: self.build_document() date = datetime.strftime(datetime.now(), '%Y-%m-%d') self._document_controller.format_for_email() self._document_controller.save(self._document_title + ' ' + date + '.docx') def pipeline_trigger(self, pipeline): """ Triggers a pipeline on Jenkins FUTURE - move out to Jenkins API wrapper """ replacements = Replacements() pipeline = replacements.replace(pipeline) matches = re.search(r'(http.*\/).*', pipeline) if matches: pipeline = matches.groups(0)[0].rstrip('/') Logger().info('Triggering pipeline {0}'.format(pipeline)) params = {} auth = (self._configuration.jenkins.user, self._configuration.jenkins.password) pipeline = '{0}/{1}'.format(pipeline, replacements.replace('{TRIGGER_URI}')) response = requests.post(pipeline, auth=auth, params=params, verify=False) if response.status_code != 200: Logger().error( 'Error whilst triggering pipeline - server returned status {0}'.format( response.status_code ) ) Logger().info('Done {0}'.format(pipeline)) @staticmethod def file_collator(path, attachments): """ This method unpacks all zip files and copies the resulting SQL into a temporary location Once complete, it repackages the temporary location and moves it into the Workspace for attachment to the release instruction email """ Logger().info('Collating SQL files from attachments') package_name = Replacements().replace('mss-platform-release-{FIX_VERSION}').replace('/', '-') destination = os.path.join(path, package_name) up_dir = os.path.join(destination, 'up') down_dir = os.path.join(destination, 'down') package_dir = os.path.join(os.getcwd(), 'packages') if not os.path.exists(package_dir): create_directory(package_dir) create_directory(destination) create_directory(up_dir) create_directory(down_dir) for attachment in attachments: if hasattr(ReleaseInstructions, attachment.extension.lower() + '_handler'): filename = os.path.join(path, attachment.filename) Logger().debug( 'Calling handler \'' + attachment.extension + '_handler\' for file ' + filename ) try: getattr(ReleaseInstructions, attachment.extension.lower() + '_handler')( destination, os.path.join(path, attachment.filename) ) except Exception as exception: Logger().error('Failed to add file \'' + filename + '\' to archive') Logger().error('reason: ' + str(exception)) filename = mkzip(destination, os.path.join(path, package_name + '.zip')) shutil.move(os.path.join(destination, filename), os.path.join(package_dir, os.path.basename(filename))) return os.path.basename(filename) @staticmethod def zip_handler(destination, filepath): temporary_directory = os.path.join(destination, 'temp') create_directory(temporary_directory) unzip(filepath, temporary_directory, flatten=True) sql_files = [ filename for filename in os.listdir( temporary_directory ) if re.search( "^.*\.sql$", filename, re.IGNORECASE ) ] if len(sql_files) == 0: Logger().error('Got zip file but it contains no SQL files. Skipping...') for sql in sql_files: ReleaseInstructions.sql_handler(destination, os.path.join(temporary_directory, sql)) shutil.rmtree(temporary_directory) @staticmethod def sql_handler(destination, filepath): try: Logger().info('Adding file \'' + filepath + '\'') sql_file = ReleaseInstructions._sql_filename(os.path.basename(filepath)) output_directory = os.path.join( destination, sql_file.direction.lower(), sql_file.ticket_order, sql_file.ticket_id, sql_file.server, sql_file.database ) create_directory(output_directory) os.rename( filepath, os.path.join( output_directory, ( sql_file.operation_order + '_' if sql_file.operation_order is not None else '' ) + sql_file.operation + '.sql' ) ) # pylint: disable=broad-except # We are not bothering to handle invalidly named SQL files - these will be # ignored from the instruction sent to SysOps. # ANY error here and the file gets bounced back to the developer. except Exception as exception: Logger().error('Failed to handle SQL file \'' + os.path.basename(filepath) + '\'') Logger().error('reason: ' + str(exception)) @staticmethod def _sql_filename(filename): SQLStructure = namedtuple( 'SQLStructure', 'ticket_order ticket_id server database direction operation_order operation' ) try: groups = ReleaseInstructions._regex.search(filename).groups() return SQLStructure( ticket_order=(groups[0] if groups[0] != '' else 'any_order'), ticket_id=groups[1], server=groups[2], database=groups[3], direction=groups[4], operation_order=(groups[5] if groups[5] != '' else None), operation=groups[6] ) except AttributeError: raise InvalidFilenameError('The filename \'' + filename + '\' does not match the anticipated format')
def __init__(self): """ initialise the ReleaseNote object """ self._document_controller = DocumentController(self.CONFIGURATION) self._configuration = self._document_controller.configuration self._document_controller.add_callback('attachments', getattr(ReleaseInstructions, 'file_collator'))