Skip to content

Commit

Permalink
Merge pull request #50 from rogers-obrien-rad/feature/no-ref/budgets
Browse files Browse the repository at this point in the history
Feature/no ref/budgets
  • Loading branch information
HagenFritz committed Nov 10, 2023
2 parents f226061 + 8d75486 commit 8b2f1ed
Show file tree
Hide file tree
Showing 11 changed files with 554 additions and 159 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: test suite

on: [push]
on:
push:
pull_request:
branches: [ main ]

jobs:
build:
Expand Down
246 changes: 246 additions & 0 deletions propycore/access/budgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
from .base import Base

import sys
import pathlib
sys.path.append(f"{pathlib.Path(__file__).resolve().parent.parent}")

from exceptions import *

class Budgets(Base):
"""
Access and working with RFIs in a given project
"""
def __init__(self, access_token, server_url) -> None:
super().__init__(access_token, server_url)

self.endpoint = "/rest/v1.0/budget_views"

def get_views(self, company_id, project_id, page=1, per_page=100):
"""
Lists the budget views
Parameters
----------
company_id : int
unique identifier for the company
project_id : int
unique identifier for the project
page : int, default 1
page number
per_page : int, default 100
number of companies to include
Returns
-------
views : list of dict
views and their corresponding meta data
"""
params = {
"project_id": project_id,
"page": page,
"per_page": per_page
}

headers = {
"Procore-Company-Id": f"{company_id}"
}

views = self.get_request(
api_url=f"{self.endpoint}",
additional_headers=headers,
params=params
)

return views

def find_view(self, company_id, project_id, identifier):
"""
Finds specified budget view
Parameters
----------
company_id : int
unique identifier for the company
project_id : int
unique identifier for the project
identifier : int or str
identifier for view which can be an id (int) or name (str)
Returns
-------
view : dict
budget view data
"""
if isinstance(identifier, int):
key = "id"
else:
key = "name"

for view in self.get_views(company_id=company_id, project_id=project_id):
if view[key] == identifier:
return view

raise NotFoundItemError(f"Could not find view {identifier}")

def get_budget_columns(self, company_id, project_id, budget_view_id):
"""
Lists the columns in a budget view
Parameters
----------
company_id : int
unique identifier for the company
project_id : int
unique identifier for the project
budget_view_id : int
unique identifier for the budget view
Returns
-------
columns : list of dict
columns in a budget view
"""
params = {
"project_id": project_id
}

headers = {
"Procore-Company-Id": f"{company_id}"
}

columns = self.get_request(
api_url=f"{self.endpoint}/{budget_view_id}/budget_detail_columns",
additional_headers=headers,
params=params
)

return columns

def find_budget_column(self, company_id, project_id, budget_view_id, identifier):
"""
Finds specified budget view column
Parameters
----------
company_id : int
unique identifier for the company
project_id : int
unique identifier for the project
budget_view_id : int
unique identifier for the budget view
identifier : int or str
identifier for view which can be an id (int) or name (str)
Returns
-------
column : dict
column data
"""
if isinstance(identifier, int):
key = "id"
else:
key = "name"

for column in self.get_budget_columns(company_id=company_id, project_id=project_id, budget_view_id=budget_view_id):
if column[key] == identifier:
return column

raise NotFoundItemError(f"Could not find column {identifier}")

def get_budget_rows(self, company_id, project_id, budget_view_id):
"""
Lists the rows in a budget view
Parameters
----------
company_id : int
unique identifier for the company
project_id : int
unique identifier for the project
budget_view_id : int
unique identifier for the budget view
Returns
-------
rows : list of dict
rows in a budget view
"""
params = {
"project_id": project_id,
"budget_row_type": "all"
}

headers = {
"Procore-Company-Id": f"{company_id}"
}

columns = self.get_request(
api_url=f"{self.endpoint}/{budget_view_id}/detail_rows",
additional_headers=headers,
params=params
)

return columns

def find_budget_row(self, company_id, project_id, budget_view_id, identifier):
"""
Finds specified budget view row
Parameters
----------
company_id : int
unique identifier for the company
project_id : int
unique identifier for the project
budget_view_id : int
unique identifier for the budget view
identifier : int or str
identifier for view which can be an id (int) or cost_code (str)
Returns
-------
column : dict
column data
"""
if isinstance(identifier, int):
key = "id"
else:
key = "cost_code"

for column in self.get_budget_rows(company_id=company_id, project_id=project_id, budget_view_id=budget_view_id):
if column[key] == identifier:
return column

raise NotFoundItemError(f"Could not find row {identifier}")

def get_budget_details(self, company_id, project_id, budget_view_id):
"""
Return a list of all rows from the Budget Detail Report for a Project and Budget View.
Parameters
----------
company_id : int
unique identifier for the company
project_id : int
unique identifier for the project
budget_view_id : int
unique identifier for the budget view
Returns
-------
"""
params = {
"project_id": project_id
}

headers = {
"Procore-Company-Id": f"{company_id}"
}

details = self.get_request(
api_url=f"{self.endpoint}/{budget_view_id}/budget_details",
additional_headers=headers,
params=params
)

return details
12 changes: 11 additions & 1 deletion propycore/access/companies.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,17 @@ def find(self, identifier):

def get_projects(self, company_id):
"""
Gets all projects from the companies
Parameters
----------
company_id : int
The identifier for the company
Returns
-------
projects : list of dict
list where each value is a project within the company
"""
endpoint = f"{self.endpoint}/{company_id}/projects"

Expand Down
4 changes: 3 additions & 1 deletion propycore/procore.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .exceptions import *
from .access import companies, generic_tools, projects, documents, rfis, directory, submittals, tasks
from .access import companies, generic_tools, projects, documents, rfis, directory, submittals, tasks, budgets
import requests
import urllib
from bs4 import BeautifulSoup
Expand Down Expand Up @@ -56,6 +56,8 @@ def __init__(self, client_id, client_secret, redirect_uri, base_url, oauth_url)
self.__users__ = directory.Users(access_token=self.__access_token, server_url=self.__base_url)
self.__vendors__ = directory.Vendors(access_token=self.__access_token, server_url=self.__base_url)
self.__trades__ = directory.Trades(access_token=self.__access_token, server_url=self.__base_url)

self.__budgets__ = budgets.Budgets(access_token=self.__access_token, server_url=self.__base_url)

def get_auth_code(self):
"""
Expand Down
93 changes: 93 additions & 0 deletions snippets/get_budget_details.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import os
import sys
import pathlib
sys.path.append(f"{pathlib.Path(__file__).resolve().parent.parent}")

from propycore.procore import Procore

from dotenv import load_dotenv
import json

if os.getenv("CLIENT_ID") is None:
load_dotenv()

if __name__ == "__main__":
connection = Procore(
client_id=os.getenv("CLIENT_ID"),
client_secret=os.getenv("CLIENT_SECRET"),
redirect_uri=os.getenv("REDIRECT_URI"),
oauth_url=os.getenv("OAUTH_URL"),
base_url=os.getenv("BASE_URL")
)

company = connection.__companies__.find(identifier="Rogers-O`Brien Construction")
project = connection.__projects__.find(
company_id=company["id"],
identifier="Sandbox Test Project"
)
budget_view = connection.__budgets__.find_view(
company_id=company["id"],
project_id=project["id"],
identifier=414205
)

# Example 1: List all columns
# ---------
print("Example 1")
budget_columns = connection.__budgets__.get_budget_columns(
company_id=company["id"],
project_id=project["id"],
budget_view_id=budget_view["id"]
)

print(f"Number of Budget Columns: {len(budget_columns)}")

# Example 2: Find column by name
# ---------
print("Example 2")
column_by_name = connection.__budgets__.find_budget_column(
company_id=company["id"],
project_id=project["id"],
budget_view_id=budget_view["id"],
identifier="Cost to Date"
)

print(json.dumps(column_by_name, indent=4))

# Example 3: List all rows
# ---------
print("Example 3")
budget_rows = connection.__budgets__.get_budget_rows(
company_id=company["id"],
project_id=project["id"],
budget_view_id=budget_view["id"]
)

print(f"Number of Budget Rows: {len(budget_rows)}")

# Example 4: Find row by name
# ---------
print("Example 4")
row_by_name = connection.__budgets__.find_budget_row(
company_id=company["id"],
project_id=project["id"],
budget_view_id=budget_view["id"],
identifier="None"
)

print(json.dumps(row_by_name, indent=4))

# Example 5: Get budget details
# ---------
# TODO: Getting ClientNotFoundError
'''
print("Example 5")
details = connection.__budgets__.get_budget_details(
company_id=company["id"],
project_id=project["id"],
budget_view_id=budget_view["id"]
)
print(f"Number of budget line items: {len(details)}")
'''

Loading

0 comments on commit 8b2f1ed

Please sign in to comment.