Skip to content

Spello Consulting Foundation Library

Available Classes

The Spello Consulting Foundation library provides some foundational classes for:

  • SCCommon: Some common functions used by other classes.
  • SCConfigManager: Reading from and validating YAML style config files
  • SCLogging: Logging messages to the console and a log file and sending email in plain text or HTML format
  • CSVReader: Reading data from and and writing to CSV files
  • DateHelper: Date and datetime helper functions
  • JSONEncoder: Helps convert dicts and lists to json and back, preserving datetime and enum data types.

Installing the library

The library is available from PyPi, so to add it to your Python project use pip:

pip install sc-foundation-services

Or better yet, use UV:

uv add sc-foundation-services

Configuration File

The library uses a YAML file for configuration. An example config file (config.yaml.example) is available on Github. Copy this to [your_app_name].yaml before using the library.

Here's the example file - the library expects to find the Files and Email sections in the file:

AmberAPI:
    APIKey: somerandomkey342
    BaseUrl: https://api.amber.com.au/v1
    Timeout: 15

Files:
    LogfileName: logfile.log
    LogfileMaxLines: 500
    LogProcessID: False
    LogfileVerbosity: detailed
    ConsoleVerbosity: detailed

Email:
    EnableEmail: True
    SMTPServer: smtp.gmail.com
    SMTPPort: 587
    SMTPUsername: me@gmail.com
    SMTPPassword: <Your SMTP password>
    SubjectPrefix: "[Bob Portfolio]: "

Configuration Parameters

Section: Files

Parameter Description
LogfileName The name of the log file, can be a relative or absolute path.
LogfileMaxLines Maximum number of lines to keep in the log file. If zero, file will never be truncated.
LogfileVerbosity The level of detail captured in the log file. One of: none; error; warning; summary; detailed; debug; all
ConsoleVerbosity Controls the amount of information written to the console. One of: error; warning; summary; detailed; debug; all. Errors are written to stderr all other messages are written to stdout

Section: Email

Parameter Description
EnableEmail Set to True if you want to allow the app to send emails. If True, the remaining settings in this section must be configured correctly.
SMTPServer The SMTP host name that supports TLS encryption. If using a Google account, set to smtp.gmail.com
SMTPPort The port number to use to connect to the SMTP server. If using a Google account, set to 587
SMTPUsername Your username used to login to the SMTP server. If using a Google account, set to your Google email address. Alternatively, set the SMTP_USERNAME environment variable.
SMTPPassword The password used to login to the SMTP server. If using a Google account, create an app password for the app at https://myaccount.google.com/apppasswords. Alternatively, set the SMTP_PASSWORD environment variable.
SubjectPrefix Optional. If set, the app will add this text to the start of any email subject line for emails it sends.

Example code

Here's an example module that shows how to use the library classes. Use the API Reference navigation to view the API methods for each class. The code example and the companion config and Excel files is available in the examples/ folder in the Github repo.


  # ruff: noqa: INP001
  """Example code using the sc-foundation libraries."""

  import datetime as dt
  import platform
  import sys

  from example_config_schemas import ConfigSchema

  from sc_foundation import CSVReader, DateHelper, SCConfigManager, SCLogger

  CONFIG_FILE = "examples/example_config.yaml"
  CSV_FILE = "examples/example_csv.csv"


  def update_csv():
      header_config = [
          {
              "name": "Symbol",
              "type": "str",
              "match": True,
              "sort": 2,
          },
          {
              "name": "Date",
              "type": "date",
              "format": "%Y-%m-%d",
              "match": True,
              "sort": 1,
              "minimum": None,
          },
          {
              "name": "Name",
              "type": "str",
          },
          {
              "name": "Currency",
              "type": "str",
          },
          {
              "name": "Price",
              "type": "float",
              "format": ".2f",
          },
      ]

      extra_data = [
          {
              "Symbol": "AAPL",
              "Date": dt.date(2023, 10, 1),
              "Name": "Apple Inc.",
              "Currency": "USD",
              "Price": 150.00,
          },
          ]

      # Create an instance of the CSVReader class
      try:
          csv_reader = CSVReader(CSV_FILE, header_config)
      except (ImportError, TypeError, ValueError) as e:
          print(e, file=sys.stderr)
          return

      csv_reader.update_csv_file(extra_data)


  def main():
      """Main function to run the example code."""
      print(f"Hello from sc-foundation running on {platform.system()}")

      # Get our default schema, validation schema, and placeholders
      schemas = ConfigSchema()

      # Initialize the SC_ConfigManager class
      try:
          config = SCConfigManager(
              config_file=CONFIG_FILE,
              default_config=schemas.default,
              validation_schema=schemas.validation,
              placeholders=schemas.placeholders
          )
      except RuntimeError as e:
          print(f"Configuration file error: {e}", file=sys.stderr)
          return

      # Print a value from the sample config file
      print(f"API key = {config.get('AmberAPI', 'APIKey')}")

      # Initialize the SC_Logger class
      try:
          logger = SCLogger(config.get_logger_settings())
      except RuntimeError as e:
          print(f"Logger initialisation error: {e}", file=sys.stderr)
          return

      logger.log_message("This is a test message at the summary level.", "summary")

      # Setup email
      email_settings = config.get_email_settings()

      if email_settings is not None:
          logger.register_email_settings(email_settings)

          text_msg = "Hello world from sc-foundation example code."
          if logger.send_email("Hello world", text_msg):
              logger.log_message("Email sent OK.", "detailed")

      # Use DateHelper to get the current date and time
      prior_date = DateHelper.today_add_days(-7)
      print(f"Prior date (7 days ago): {prior_date}")

      # Update a CSV file
      update_csv()

      # See if we have a fatal error from a previous run
      if logger.get_fatal_error():
          print("Prior fatal error detected.")
          logger.clear_fatal_error()


  if __name__ == "__main__":
      main()