Table of Contents

How To: Export Data To Microsoft Excel

For reporting purposes it is often necessary to automatically export data into Microsoft Office Excel. You could for instance create a timesheet export script that automatically creates an excel pivot table report per user and sends this report via email.

time cockpit supports two ways of exporting data to Microsoft Office Excel:

  • Use time cockpit's built-in export function.
  • In case you need something very special (e.g. special formatting of the resulting Microsoft Office Excel file) you can use Microsoft Office automation in IronPython.

Using Built-In Microsoft Office Excel Export Function

The following sample shows how to export the result of a TCQL query using an export template file inside a script:

Note

The Export method shown in the code below expects either a template file or a list definition. If you specify a template the list definition is ignored. If you do not specify a template file you have to specify a list definition (i.e. template is automatically built based on the list definition).

clr.AddReference("TimeCockpit.Data.Export")
from TimeCockpit.Data.Export.Excel import *
from System.Threading import CancellationToken

resultingRows = Context.Select("From T In Timesheet Where T.APP_BeginTime > #2010-01-01# Select T")
sourceList = Context.Model.ModelEntityViews["APP_TimesheetList"].Configuration.Columns

XlsxExporter2.ExportToFile(
  "c:\\temp\\Template.xlsx",    # Path to template file  
  "c:\\temp\\export.xlsx",      # Target file name
  resultingRows,                # Rows that should be exported (has to be a list of entity objects)
  sourceList,                   # List definition that acts as the source for the export
  None,                         # Reserved; always pass None here
  CancellationToken(False))     # Cancellation token that could be used to cancel the export

Using Microsoft Office Automation in a Script

The following sample shows how to generate an Microsoft Office Excel file using Microsoft Office automation. The script loads a template workbook, creates one sheet per user by copying a worksheet and fills the sheet with time sheet rows. Holidays and weekends are specially formatted.

import clr
clr.AddReferenceByName('Microsoft.Office.Interop.Excel, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c')
clr.AddReference("System.Core")
from Microsoft.Office.Interop import *
from Microsoft.Office.Interop.Excel import *
from System import *
from System.Linq import Enumerable

# Helper function that sets the background of the entire row 
def ColorizeRow(cell):
  cell.EntireRow.Interior.ThemeColor = XlThemeColor.xlThemeColorDark1
  cell.EntireRow.Interior.TintAndShade = -0.249977111117893 

# Helper function that checks if a certain day if no working day 
def IsDayOff(day, holidays):
  return day.DayOfWeek == DayOfWeek.Saturday or day.DayOfWeek == DayOfWeek.Sunday \
    or Enumerable.Count(holidays, lambda h: h.LegalHolidayDate.Day == day.Day) > 0 

# Helper function that colorizes the entire day if the day is off 
def HandleRowColor(currentDayDate, cell, holidays):
  if IsDayOff(currentDayDate, holidays):
    ColorizeRow(cell)

# Creates an Excel worksheet for a specific user and month 
def AddWorksheet(user, year, month, workbook):
  # Get all timesheets for the user
  timesheets = Context.SelectWithParams({
    "Query": "From T In Timesheet Where T.UserDetail.Username = @Username And :Year(T.BeginTime) = @YearFilter And :Month(T.BeginTime) = @MonthFilter Order By T.BeginTime Select T",
    "@Username": user.Username,
    "@YearFilter": Decimal(year),
    "@MonthFilter": Decimal(month) })

  # Get holidays for the corresponding month
  holidays = Context.SelectWithParams({
    "Query": "From H In LegalHoliday Where :Year(H.LegalHolidayDate) = @YearFilter And :Month(H.LegalHolidayDate) = @MonthFilter Order By H.LegalHolidayDate Select H",
    "@YearFilter": Decimal(year),
    "@MonthFilter": Decimal(month) })

  # Add sheet for user
  workbook.Worksheets["Tabelle1"].Copy(Before = workbook.Worksheets[1])
  ws = workbook.Worksheets[1]
  ws.Name = user.Lastname + ", " + user.Firstname
  ws.Activate()

  rowIndex = 2
  totalWorkingHours = 0
  hoursPerDay = user.WeeklyHoursOfWork / 5
  prevDay = 0 

  # Loop over all days of the month 
  for currentDay in range(1, DateTime(year, month, 1).AddMonths(1).AddDays(-1).Day + 1):
    currentDayDate = DateTime(year, month, currentDay)
    dailyTimesheets = Enumerable.Where(timesheets, lambda t: t.BeginTime.Day == currentDay)

    if Enumerable.Count(dailyTimesheets) > 0:
      # There are timesheets for this day 
      for timesheet in dailyTimesheets:
        # Add row for timesheet
        HandleRowColor(currentDayDate, ws.Cells[rowIndex, 1], holidays)
        if prevDay != timesheet.BeginTime.Day:
          # Don't repeat day index for mulitple rows of the same day
          ws.Cells[rowIndex, 1].Value2 = timesheet.BeginTime.Day
        ws.Cells[rowIndex, 2].Value2 = timesheet.BeginTime
        ws.Cells[rowIndex, 3].Value2 = timesheet.EndTime
        ws.Cells[rowIndex, 4].Value2 = timesheet.DurationInHours / 24
        ws.Cells[rowIndex, 5].Value2 = timesheet.Description
        totalWorkingHours = totalWorkingHours + timesheet.DurationInHours
        prevDay = timesheet.BeginTime.Day
        rowIndex = rowIndex + 1 
    else:
      # There are no timesheets for this day
      HandleRowColor(currentDayDate, ws.Cells[rowIndex, 1], holidays)
      ws.Cells[rowIndex, 1].Value2 = currentDay 
      rowIndex = rowIndex + 1 

  # Add sums
  ws.Cells[rowIndex,2].Value2 = 'Gesamt:'
  ws.Cells[rowIndex,4].Value2 = (totalWorkingHours)/ 24
  rowIndex = rowIndex + 1 

# Open Excel (visible just for demo purposes)
ex = Excel.ApplicationClass()   
ex.Visible = True
ex.DisplayAlerts = False   
workbook = ex.Workbooks.Open(r"c:\temp\MonthlyReport.xlsx")

users = Context.Select("From U In UserDetail Order By U.Username Select U")
for user in users:
  AddWorksheet( user, 2017, 5, workbook )

print "Done!"

Using an Action to Integrate Microsoft Office Excel Export in the User Interface

You can convert an export script into an Action to offer a button in time cockpit's user interface that exports the data of the selected row in a list. The following example shows how the sample shown above can be added as an action to the entity APP_UserDetail:

Note

Note that the action's code must only contain one top level def statement! If you need helper functions you have to define them as nested functions inside the one and only top level function (see sample code below).

import clr
clr.AddReferenceByName('Microsoft.Office.Interop.Excel, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c')
clr.AddReference("System.Core")
from Microsoft.Office.Interop import *
from Microsoft.Office.Interop.Excel import *
from System import *
from System.Linq import Enumerable

def ExportTimesheetProtocolToExcel(actionContext):
  # Helper function that sets the background of the entire row 
  def ColorizeRow(cell):
    cell.EntireRow.Interior.ThemeColor = XlThemeColor.xlThemeColorDark1
    cell.EntireRow.Interior.TintAndShade = -0.249977111117893 

  # Helper function that checks if a certain day if no working day 
  def IsDayOff(day, holidays):
    return day.DayOfWeek == DayOfWeek.Saturday or day.DayOfWeek == DayOfWeek.Sunday \
      or Enumerable.Count(holidays, lambda h: h.LegalHolidayDate.Day == day.Day) > 0 

  # Helper function that colorizes the entire day if the day is off 
  def HandleRowColor(currentDayDate, cell, holidays):
    if IsDayOff(currentDayDate, holidays):
      ColorizeRow(cell)

  # Creates an Excel worksheet for a specific user and month 
  def AddWorksheet(user, year, month, workbook):
    # Get all timesheets for the user
    timesheets = Context.SelectWithParams({
      "Query": "From T In Timesheet Where T.UserDetail.Username = @Username And :Year(T.BeginTime) = @YearFilter And :Month(T.BeginTime) = @MonthFilter Order By T.BeginTime Select T",
      "@Username": user.Username,
      "@YearFilter": Decimal(year),
      "@MonthFilter": Decimal(month) })

    # Get holidays for the corresponding month
    holidays = Context.SelectWithParams({
      "Query": "From H In LegalHoliday Where :Year(H.LegalHolidayDate) = @YearFilter And :Month(H.LegalHolidayDate) = @MonthFilter Order By H.LegalHolidayDate Select H",
      "@YearFilter": Decimal(year),
      "@MonthFilter": Decimal(month) })

    # Add sheet for user
    workbook.Worksheets["Tabelle1"].Copy(Before = workbook.Worksheets[1])
    ws = workbook.Worksheets[1]
    ws.Name = user.Lastname + ", " + user.Firstname
    ws.Activate()

    rowIndex = 2
    totalWorkingHours = 0
    hoursPerDay = user.WeeklyHoursOfWork / 5
    prevDay = 0 

    # Loop over all days of the month 
    for currentDay in range(1, DateTime(year, month, 1).AddMonths(1).AddDays(-1).Day + 1):
      currentDayDate = DateTime(year, month, currentDay)
      dailyTimesheets = Enumerable.Where(timesheets, lambda t: t.BeginTime.Day == currentDay)

      if Enumerable.Count(dailyTimesheets) > 0:
        # There are timesheets for this day 
        for timesheet in dailyTimesheets:
          # Add row for timesheet
          HandleRowColor(currentDayDate, ws.Cells[rowIndex, 1], holidays)
          if prevDay != timesheet.BeginTime.Day:
            # Don't repeat day index for mulitple rows of the same day
            ws.Cells[rowIndex, 1].Value2 = timesheet.BeginTime.Day
          ws.Cells[rowIndex, 2].Value2 = timesheet.BeginTime
          ws.Cells[rowIndex, 3].Value2 = timesheet.EndTime
          ws.Cells[rowIndex, 4].Value2 = timesheet.DurationInHours / 24
          ws.Cells[rowIndex, 5].Value2 = timesheet.Description
          totalWorkingHours = totalWorkingHours + timesheet.DurationInHours
          prevDay = timesheet.BeginTime.Day
          rowIndex = rowIndex + 1 
      else:
        # There are no timesheets for this day
        HandleRowColor(currentDayDate, ws.Cells[rowIndex, 1], holidays)
        ws.Cells[rowIndex, 1].Value2 = currentDay 
        rowIndex = rowIndex + 1 

    # Add sums
    ws.Cells[rowIndex,2].Value2 = 'Gesamt:'
    ws.Cells[rowIndex,4].Value2 = (totalWorkingHours)/ 24
    rowIndex = rowIndex + 1 

  # make sure that input set contains at least one object 
  if (Enumerable.Count[EntityObject](actionContext.InputSet) > 0):
    # Open Excel (visible just for demo purposes)
    ex = Excel.ApplicationClass()   
    ex.Visible = True
    ex.DisplayAlerts = False   
    workbook = ex.Workbooks.Open(r"C:\temp\MonthlyReport.xlsx")

    for user in actionContext.InputSet:
      AddWorksheet( user, 2017, 5, workbook )