Time Reports for Farm Payroll

I just wanted to see if anyone is using FarmOS to track hours for there Payroll? We currently are at Silver Creek Nursery and have been for the past several months. We are really excited with the ability to link it to assets and keep better farm records. We have grand plans to create assets for each of our trees in the orchard (we have over 200 different varieties of apples and other tress). This way we can do bloom tracking, harvest tracking for each cultivar.

Anyway, currently I am trying to streamline payroll. Right now I have to export two CSV’s. One for logs and one for Qty’s and then join them in excel to figure out the hours for payroll. I have been learning python and using the FarmOS.py API to create a simple UI for time logs. I am trying to get a payroll report in a touch of a button using the FarmOS.py API but I am struggling with filters. I have had success with simple filters but struggle with more complex ones like log timestamp dates between ranges.

I was wondering if anyone had some insight about how to go about this or if FarmOS.py is even the best tool for the job.

4 Likes

Have you ever used node-red? It may be simpler for building UI’s for interacting with the API.
@pat may have something similar built already.

This may also be another nice use case for AssetLink @Symbioquine? Perhaps not a mobile field tool use case but not sure that needs to be a constraint either?

2 Likes

I have not used node-red. I have looked into it a little bit. Would your website have a good node-red getting started with farmOS example?

2 Likes

NVM I just found this topic Node-Red example flows for beginners

2 Likes

Haven’t much on my site at the moment, I must add to it though.
I made a couple of videos on getting started, https://youtube.com/playlist?list=PLV_VqXF2pXXJdFjVhzdzDKskzkeXqLd8f

I’d actually forgotten about that thread, it’s probably the best place for some basic flows.

2 Likes

Yea. That’s right. Now the users logs their times, and I get a notification with a summary every time, and the users get’s their summary pr mail every time they log hours. They’re also provided with a list view of unpaid hours while adding new ones.
Could need some refinement, but it works fine.

I love farmOS, but it’s (yet) painful to add logs of some complexity. Node-Red helps a lot to make complex logging super-easy with any detail level you can think of. It’s for sure well spent time to study Node-Red.

I’ll share the flow on github eventually. Just been too busy.

2 Likes

Cross-posing my comment from another thread:

1 Like

Hi Just though I’d post my solution for the time sheets reports. I am quit happy with how it turned out. It could be modified for other qty reports too. I ended up staying with python. The [Intro to Python Blog Post] and a lot of time figuring out how how filters work for api calls later and I was able to do it.

# farmOS Time Sheet Report Code
# By: Zack Muma
# Date: 4/9/2023


# include packages
from farmOS import farmOS
from datetime import datetime, timezone
import csv
import pandas as pd


# assign login variables
hostname = ""
username = ""
password = ""

#create client
farm_client = farmOS(hostname)

# Authorize the client, save the token.
token = farm_client.authorize(username, password)


#convert date range to timestamp
startDate = datetime(2023,3,26,0) # start date input
endDate = datetime(2023,4,8,23,59,59) # end date input 
startDatets = datetime.timestamp(startDate)
endDatets = datetime.timestamp(endDate)
#print(datetime.fromtimestamp(startDate))

#get name id's
Names = [","] #Put Names of users wanted for reports here.  Must be as shown in user asset
filters = farm_client.filter('name',Names, "IN")

#get all log hours between star and end date
for name_id in farm_client.resource.iterate('user', params=filters):
    csvRow = []
    filters = {
            **farm_client.filter('owner.id',name_id['id']),
            **farm_client.filter('timestamp',[(str(int(startDatets))),(str(int(endDatets)))],"BETWEEN"),
            }
    for response2 in farm_client.log.iterate('activity', params=filters):

        
        for qty_id in response2['relationships']['quantity']['data']:
            filterQty = {
                **farm_client.filter('id',qty_id['id']),
                **farm_client.filter('units.id','Unit ID here'), # put the id for the unit term wanted here
                }
            response3 = farm_client.resource.get('quantity', 'standard',params = filterQty)
                        
            for value in response3['data']:
                logtime = datetime.fromisoformat(response2["attributes"]['timestamp'])
                csvRow.append((response2["attributes"]['name'], logtime.astimezone().strftime('%d/%m/%Y'), logtime.astimezone().strftime('%A'), value['attributes']['value']['decimal']))

    #create data frame for logs
    df = pd.DataFrame(csvRow, columns=["Log", "Date","Weekday", 'Hours'])
    df['Hours'] = df['Hours'].apply(pd.to_numeric)
    df['Date'] = pd.to_datetime(df['Date'],dayfirst = True)
    df.sort_values(['Date'],inplace = True)
    strHTML = df.to_html(index = False)
    
    #create HTML file
    path2 = f"" #put location for html file
    file = open(path2, 'w')

    #create header and imput first table into html file
    file.write(f"<h1>Timesheet for {name_id['attributes']['name']}</h1>")
    file.write(f"<h3>This pay period {startDate} to {endDate}</h3>") 
    file.write(strHTML)
    
    #creating and writing second table to html file
    sumHours = df.groupby(['Date','Weekday'], as_index = False)['Hours'].sum()
    sumHours['Lunch Hours'] = 0
    sumHours.loc[sumHours['Hours'] > 5 ,'Lunch Hours'] = 0.5
    sumHours['Total'] = sumHours['Hours'] + sumHours['Lunch Hours']
    strHTML = sumHours.to_html(index = False)
    file.write(f"<h3>Hours by Day</h3>")  
    file.write(strHTML)

    #addind up total hours and writing to html table
    totalHours = sumHours['Total'].sum()    
    file.write(f"<h3>Total hours this pay period: {totalHours} hrs</h3>")
    file.close()

Here is what the output looks like. Its an HTML file.
Screenshot 2023-04-09 185901

3 Likes

That’s awesome @Zack! Great to see more real-world examples of folks using the API!


P.S. Hope you don’t mind, I edited your post so the python syntax would get highlighted better by the forum.

```python
# Python code goes here!
import antigravity

print("Hello World!")
```

Yields:

# Python code goes here!
import antigravity

print("Hello World!")
3 Likes

Thanks! Hope its helps others. Nope no problem at all! Thanks for updating the post.

3 Likes