violet_rails icon indicating copy to clipboard operation
violet_rails copied to clipboard

BillablesReport Plugin : External API Connection for sending report of billables per client

Open alis-khadka opened this issue 2 years ago • 0 comments
trafficstars

BillablesReport Plugin

For the latest version of this plugin see here: https://github.com/restarone/violet_rails/blob/master/test/plugin_fixtures/billables_report_plugin.yml

search for BillablesReportPlugin

    class BillablesReportPlugin
      def initialize(parameters)  
        @external_api_client = parameters[:external_api_client]
        @clients_report = {}
      end
    
      def start
        reporting_emails = @external_api_client.metadata["REPORTING_EMAILS"]
        raise 'REPORTING_EMAILS are missing!' if reporting_emails.blank?
    
        time_tracker = ApiNamespace.find_by(name: 'time_tracker')
        clients = time_tracker.properties['for_what_client']
        current_time = Time.zone.now
    
        clients.each do |client_name|
          # Fetching tracker entries for today, week and month to date.
          time_tracker_entries_for_day = fetch_tracker_entries(
            client_name,
            time_tracker,
            current_time - 24.hours,
            current_time
          )
          time_tracker_entries_for_week = fetch_tracker_entries(
            client_name,
            time_tracker,
            current_time.beginning_of_week.beginning_of_day,
            current_time
          )
          time_tracker_entries_for_month = fetch_tracker_entries(
            client_name,
            time_tracker,
            current_time.beginning_of_month.beginning_of_day,
            current_time
          )
    
          @clients_report[client_name] = {}
    
          # constructing the data in CSV format
          total_hours_billed_for_day_csv_string = generate_csv_content(time_tracker_entries_for_day, client_name, 'day')
          total_hours_billed_for_week_csv_string = generate_csv_content(time_tracker_entries_for_week, client_name, 'week')
          total_hours_billed_for_month_csv_string = generate_csv_content(time_tracker_entries_for_month, client_name, 'month')
    
          # Attachment details
          total_hours_billed_for_day_attachment_details = attachment_details(
            client_name,
            "total_hours_billed_for_day(#{(current_time - 24.hours).strftime("%A, %B #{(current_time - 24.hours).day.ordinalize} %Y")} - #{current_time.strftime("%A, %B #{current_time.day.ordinalize} %Y")}).csv",
            total_hours_billed_for_day_csv_string
          )
          total_hours_billed_for_week_attachment_details = attachment_details(
            client_name,
            "total_hours_billed_for_week(#{current_time.beginning_of_week.beginning_of_day.strftime("%A, %B #{current_time.beginning_of_week.beginning_of_day.day.ordinalize} %Y")} - #{current_time.strftime("%A, %B #{current_time.day.ordinalize} %Y")}).csv",
            total_hours_billed_for_week_csv_string
          )
          total_hours_billed_for_month_attachment_details = attachment_details(
            client_name,
            "total_hours_billed_for_month(#{current_time.beginning_of_month.beginning_of_day.strftime("%A, %B #{current_time.beginning_of_month.beginning_of_day.day.ordinalize} %Y")} - #{current_time.strftime("%A, %B #{current_time.day.ordinalize} %Y")}).csv",
            total_hours_billed_for_month_csv_string
          )
    
          # Uploading respective blobs
          @clients_report[client_name][:total_hours_billed_for_day_blob] = ActiveStorage::Blob.create_and_upload!(
            io: StringIO.new(total_hours_billed_for_day_attachment_details[:content]),
            filename: total_hours_billed_for_day_attachment_details[:filename],
            content_type: total_hours_billed_for_day_attachment_details[:mime_type],
            metadata: nil
          )
          @clients_report[client_name][:total_hours_billed_for_week_blob] = ActiveStorage::Blob.create_and_upload!(
            io: StringIO.new(total_hours_billed_for_week_attachment_details[:content]),
            filename: total_hours_billed_for_week_attachment_details[:filename],
            content_type: total_hours_billed_for_week_attachment_details[:mime_type],
            metadata: nil
          )
          @clients_report[client_name][:total_hours_billed_for_month_blob] = ActiveStorage::Blob.create_and_upload!(
            io: StringIO.new(total_hours_billed_for_month_attachment_details[:content]),
            filename: total_hours_billed_for_month_attachment_details[:filename],
            content_type: total_hours_billed_for_month_attachment_details[:mime_type],
            metadata: nil
          )
        end
    
        # Building Email details
        subject = "Report of hours logged generated at: #{current_time.strftime("%A, %B #{current_time.day.ordinalize} %Y at%l:%M%p %Z")}"
        email_content = <<-HTML
        <div class='trix-content'>
          <div>Please find the reports of hours logged for different clients listed below;<br><br>
          </div>
          <ol>
            #{generate_email_content_for_clients}
          </ol>
        </div>
        HTML
    
        # Sending email report
        email_thread = MessageThread.create!(recipients: [reporting_emails], subject: subject)
        email_message = email_thread.messages.create!(
          content: email_content.html_safe,
          attachments: email_attachments
        )
      end
    
      private
      def fetch_tracker_entries(client, tracker, start_time, end_time)
        tracker.api_resources.where(
          "properties ->> 'for_what_client' = ? AND created_at >= ? AND created_at <= ?",
          client,
          start_time,
          end_time
        ).order(:created_at)
      end
    
      def generate_csv_content(tracker_entries, client, time_period)
        total_hours = tracker_entries.sum { |entry| entry.properties['how_much_time_in_hours_spent'].to_f }
        @clients_report[client]["total_hours_billed_for_#{time_period}".to_sym] = total_hours
    
        CSV.generate do |csv|
          csv << ['', 'Total Hours', total_hours]
          csv << ['', '', '']
          csv << ['Date', 'Hours', 'User', 'Client', 'Task', 'Notes']
          tracker_entries.each do |entry|
            csv << [
              entry.created_at.to_s,
              entry.properties['how_much_time_in_hours_spent'].to_f,
              entry.properties['email_address'] || entry.user&.email,
              entry.properties['for_what_client'],
              entry.properties['what_task_did_you_work_on'],
              entry.properties['notes']
            ]
          end
        end
      end
    
      def attachment_details(client, filename, attachment_content)
        {
          filename: "(#{client})-#{filename}",
          mime_type: 'text/csv',
          content: attachment_content.html_safe
        }
      end
    
      def blob_content(blob)
        ActionText::Content.new("<action-text-attachment sgid='#{blob.attachable_sgid}'></action-text-attachment>").to_s
      end
    
      def generate_email_content_for_clients
        clients = @clients_report.keys
        email_report_content = ''
    
        clients.each do |client_name|
          email_report_content += <<-CLIENT_REPORT
          <li><strong>#{client_name}</strong>
            <ul>
              <li>Total hours logged for today: #{@clients_report[client_name][:total_hours_billed_for_day]} hours<br>#{blob_content(@clients_report[client_name][:total_hours_billed_for_day_blob])}
              </li>
              <li>Total hours logged for this week to date: #{@clients_report[client_name][:total_hours_billed_for_week]} hours<br>#{blob_content(@clients_report[client_name][:total_hours_billed_for_week_blob])}
              </li>
              <li>Total hours logged for this month to date: #{@clients_report[client_name][:total_hours_billed_for_month]} hours<br>#{blob_content(@clients_report[client_name][:total_hours_billed_for_month_blob])}
              </li>
            </ul>
          </li>
          CLIENT_REPORT
        end
    
        email_report_content
      end
    
      def email_attachments
        @clients_report.values.map { |client_blobs| client_blobs.except(:total_hours_billed_for_day, :total_hours_billed_for_week, :total_hours_billed_for_month).values }.flatten
      end
    end
    # at the end of the file we have to implicitly return the class 
    BillablesReportPlugin

The following metadata needs to be included for the plugin to work: Screenshot 2023-04-18 at 7 27 30 PM

REPORTING_EMAILS : A list (array) of emails to which the report of billables should be sent.

Output

Screenshot 2023-04-18 at 7 43 25 PM

alis-khadka avatar Apr 18 '23 14:04 alis-khadka