text: # -----------------------------------------------------------------------------
  # Copyright (c) 2014--, The Qiita Development Team.
  #
  # Distributed under the terms of the BSD 3-clause License.
  #
  # The full license is in the file LICENSE, distributed with this software.
  # -----------------------------------------------------------------------------
  from smtplib import SMTP, SMTP_SSL, SMTPException
  from future import standard_library
  from functools import wraps
  from os.path import dirname
  from git import Repo
  from git.exc import InvalidGitRepositoryError
  
  from qiita_core.qiita_settings import qiita_config, r_client
  from qiita_pet import __version__ as qiita_pet_lib_version
  import qiita_db as qdb
  
  with standard_library.hooks():
      from email.mime.multipart import MIMEMultipart
      from email.mime.text import MIMEText
  
  
  def send_email(to, subject, body):
      # create email
      msg = MIMEMultipart()
      msg['From'] = qiita_config.smtp_email
      msg['To'] = to
      msg['Subject'] = subject
      msg.attach(MIMEText(body, 'plain'))
  
      # connect to smtp server, using ssl if needed
      if qiita_config.smtp_ssl:
          smtp = SMTP_SSL()
      else:
          smtp = SMTP()
      smtp.set_debuglevel(False)
      smtp.connect(qiita_config.smtp_host, qiita_config.smtp_port)
      # try tls, if not available on server just ignore error
      try:
          smtp.starttls()
      except SMTPException:
          pass
      smtp.ehlo_or_helo_if_needed()
  
      if qiita_config.smtp_user:
          smtp.login(qiita_config.smtp_user, qiita_config.smtp_password)
  
      # send email
      try:
          smtp.sendmail(qiita_config.smtp_email, to, msg.as_string())
      except Exception:
          raise RuntimeError("Can't send email!")
      finally:
          smtp.close()
  
  
  def is_test_environment():
      """Checks if Qiita is running in a test environment
  
      Returns
      -------
      bool
          Whether Qiita is running in a test environment or not
  
      Notes
      -----
      Qiita is running in a test environment if:
          - It is connected to a test database, AND
          - The config file indicates that this is a test environment
      """
      # Check that we are not in a production environment
      conn_handler = qdb.sql_connection.SQLConnectionHandler()
      # It is possible that we are connecting to a production database
      test_db = conn_handler.execute_fetchone("SELECT test FROM settings")[0]
      # Or the loaded configuration file belongs to a production environment
      return qiita_config.test_environment and test_db
  
  
  def qiita_test_checker(test=False):
      """Decorator that allows the execution of all methods in a test class only
      and only if Qiita is set up to work in a test environment.
  
      Parameters
      ----------
      test : bool, optional
          If True it will raise a RuntimeError error
  
      Raises
      ------
      RuntimeError
          If Qiita is set up to work in a production environment
      """
      def class_modifier(cls):
          if not is_test_environment() or test:
              raise RuntimeError("Working in a production environment. Not "
                                 "executing the tests to keep the production "
                                 "database safe.")
  
          # Now, we decorate the setup and teardown functions
          class DecoratedClass(cls):
              def setUp(self):
                  super(DecoratedClass, self).setUp()
                  self.conn_handler = qdb.sql_connection.SQLConnectionHandler()
  
              @classmethod
              @qdb.environment_manager.reset_test_database
              def tearDownClass(cls):
                  pass
  
          return DecoratedClass
      return class_modifier
  
  
  def execute_as_transaction(func):
      """Decorator to make a method execute inside a transaction"""
      @wraps(func)
      def wrapper(*args, **kwargs):
          from qiita_db.sql_connection import TRN
          with TRN:
              return func(*args, **kwargs)
      return wrapper
  
  
  def get_qiita_version():
      """Returns the Qiita version and Git sha if present
  
      Returns
      ------
      tuple (version, sha)
          The Qiita version and SHA. SHA can be an empty string.
      """
      # the actual repo is the abspath of the current file without
      # qiita_core
      git_repo_path = dirname(dirname(__file__))
  
      try:
          repo = Repo(git_repo_path)
          sha = repo.active_branch.commit.hexsha
      except (InvalidGitRepositoryError, TypeError):
          sha = ''
  
      return (qiita_pet_lib_version, sha)
  
  
  def get_release_info(study_status='public'):
      """Returns the study status release MD5
  
      Parameters
      ----------
      study_status : str, optional
          The study status to search for. Note that this should always be set
          to 'public' but having this exposed helps with testing. The other
          options are 'private' and 'sandbox'
  
      Returns
      ------
      str, str, str
          The release MD5, filepath and timestamp
      """
      portal = qiita_config.portal
      md5sum = r_client.get('%s:release:%s:md5sum' % (portal, study_status))
      filepath = r_client.get('%s:release:%s:filepath' % (portal, study_status))
      timestamp = r_client.get('%s:release:%s:time' % (portal, study_status))
      if md5sum is None:
          md5sum = ''
      if filepath is None:
          filepath = ''
      if timestamp is None:
          timestamp = ''
  
      return md5sum, filepath, timestamp
  