Skip to content

Commit

Permalink
Merge pull request #76 from rogers-obrien-rad/feature/no-ref/dc-php
Browse files Browse the repository at this point in the history
Feature/no ref/dc php
  • Loading branch information
HagenFritz authored Aug 9, 2024
2 parents 46ce4ad + d0f2764 commit 0ea9b81
Show file tree
Hide file tree
Showing 12 changed files with 330 additions and 43 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,6 @@ replay_pid*
# VS Code
*.code-workspace


# PHP
*vendor*
*.lock
47 changes: 47 additions & 0 deletions ProPyCore/access/direct_costs.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,51 @@ def update(self, company_id, project_id, direct_cost_id, direct_cost_data={}, li
data=payload,
)

return response

def add_attachment(self, company_id, project_id, direct_cost_id, attachments):
"""
Creates a new Direct Cost item in the specified Project.
Parameters
----------
company_id : int
unique identifier for the company
project_id : int
unique identifier for the project
direct_cost_id : int
unique identifier for the direct cost
attachments : list, default []
list of attachment file paths
Returns
-------
response : dict
response from the API containing the created Direct Cost item
"""
headers = {
"Procore-Company-Id": f"{company_id}",
"Accept": "application/json",
}

# Prepare attachments
files = []
for attachment in attachments:
mime_type, _ = mimetypes.guess_type(attachment)
if mime_type is None:
mime_type = 'application/octet-stream'
files.append(('attachments[]', (attachment, open(attachment, 'rb'), mime_type)))

# Make the request
response = self.patch_request(
api_url=f"{self.endpoint}/{project_id}/direct_costs/{direct_cost_id}",
additional_headers=headers,
data={},
files=files
)

# Close the file objects
for file_tuple in files:
file_tuple[1][1].close()

return response
47 changes: 8 additions & 39 deletions ProPyCore/procore.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from .exceptions import *
from .access import companies, generic_tools, projects, documents, rfis, directory, submittals, tasks, budgets, direct_costs
import requests
import urllib
from bs4 import BeautifulSoup

class Procore:
"""
Expand Down Expand Up @@ -59,36 +57,8 @@ def __init__(self, client_id, client_secret, redirect_uri, base_url, oauth_url)
self.budgets = budgets.Budgets(access_token=self.__access_token, server_url=self.__base_url)
self.direct_costs = direct_costs.DirectCosts(access_token=self.__access_token, server_url=self.__base_url)

def get_auth_code(self):
def get_access_token(self):
"""
Gets the 10-minute temporary authorization token
"""
# create url
params = {
"client_id": self.__client_id,
"response_type": "code",
"redirect_uri": self.__redirect_uri
}
url = self.__oauth_url + "/oauth/authorize?" + urllib.parse.urlencode(params)
# GET
response = requests.get(url, headers={
"content-type": "application/json"
})

auth_code = None # pre-allocate
if response.ok:
# use BS to parse the code from the returned html
soup = BeautifulSoup(response.text, 'html.parser')
for tag in soup.find_all("meta"):
if tag.get("name", None) == "csrf-token":
auth_code = tag.get("content", None)
else:
raise_exception(response=response)

return auth_code

def get_access_token(self, code):
'''
Gets access token from authorization code previously obtained from the get_auth_code call.
Parameters
Expand All @@ -100,23 +70,22 @@ def get_access_token(self, code):
-------
<access_token> : str
2-hour access token
'''
"""
client_auth = requests.auth.HTTPBasicAuth(self.__client_id, self.__client_secret)
post_data = {"grant_type": "client_credentials",
"code": code,
"redirect_uri": self.__redirect_uri
}
post_data = {
"grant_type": "client_credentials",
"redirect_uri": self.__redirect_uri
}
response = requests.post(self.__base_url+"/oauth/token", auth=client_auth, data=post_data)
response_json = response.json()

return response_json["access_token"]#, response_json['created_at']
return response_json["access_token"]

def reset_access_token(self):
"""
Gets a new access token
"""
temp_code = self.get_auth_code()
self.__access_token = self.get_access_token(temp_code)
self.__access_token = self.get_access_token()

def print_attributes(self):
"""
Expand Down
64 changes: 64 additions & 0 deletions php/add_attachment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

require 'get_access_token.php';

function addAttachment($access_token, $company_id, $project_id, $direct_cost_id, $attachments = []) {
$url = "https://api.procore.com/rest/v1.1/projects/$project_id/direct_costs/$direct_cost_id";

$headers = [
"Authorization: Bearer $access_token",
"Procore-Company-Id: $company_id",
"Accept: application/json"
];

$postFields = [];

// Create a new finfo resource
$finfo = finfo_open(FILEINFO_MIME_TYPE);

// Prepare attachments
foreach ($attachments as $index => $attachment) {
$mime_type = finfo_file($finfo, $attachment);
if ($mime_type === false) {
$mime_type = 'application/octet-stream';
}

$postFields["attachments[$index]"] = new CURLFile($attachment, $mime_type, basename($attachment));
}

// Close the finfo resource
finfo_close($finfo);

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);

$response = curl_exec($ch);

if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
return null;
}

curl_close($ch);

return json_decode($response, true);
}

// Main execution
$access_token = getAccessToken($client_id, $client_secret);

$company_id = 8089;
$project_id = 2783683;
$direct_cost_id = 95483758;
$attachments = [
"C:\Users\hfritz\OneDrive - RO\Documents\packages\ProPyCore\php\direct_costs_module.pdf",
];

$response = addAttachment($access_token, $company_id, $project_id, $direct_cost_id, $attachments);
echo "Response from adding attachment: \n";
print_r($response);
5 changes: 5 additions & 0 deletions php/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"require": {
"vlucas/phpdotenv": "^5.6"
}
}
File renamed without changes.
47 changes: 47 additions & 0 deletions php/get_access_token.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

require 'vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__));
$dotenv->load();

$client_id = getenv('CLIENT_ID');
$client_secret = getenv('CLIENT_SECRET');

function getAccessToken($client_id, $client_secret) {
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "https://login.procore.com/oauth/token");
curl_setopt($ch, CURLOPT_POST, 1);

$postFields = [
'grant_type' => 'client_credentials',
'client_id' => $client_id,
'client_secret' => $client_secret,
'redirect_uri' => 'urn:ietf:wg:oauth:2.0:oob'
];

curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);

if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
return null;
}

$response_data = json_decode($response, true);
curl_close($ch);

return $response_data['access_token'];
}

// Main execution
$access_token = getAccessToken($client_id, $client_secret);

if ($access_token) {
echo "Successfully obtained access token";
} else {
echo "Failed to obtain access token.";
}
43 changes: 43 additions & 0 deletions php/get_companies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

require 'get_access_token.php'; // Ensure this path is correct based on your directory structure

function getCompanies($access_token, $page = 1, $per_page = 100) {
$endpoint = "https://api.procore.com/rest/v1.0/companies";
$params = [
"page" => $page,
"per_page" => $per_page,
"include_free_companies" => "true"
];

$query = http_build_query($params);
$url = $endpoint . '?' . $query;

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $access_token,
'Content-Type: application/json'
]);

$response = curl_exec($ch);

if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
return null;
}

$companies = json_decode($response, true);
curl_close($ch);

return $companies;
}

// Main execution
$access_token = getAccessToken($client_id, $client_secret);
$companies = getCompanies($access_token);

echo "Companies: \n";
print_r($companies);
56 changes: 56 additions & 0 deletions php/get_direct_costs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

require 'get_access_token.php'; // Ensure this path is correct based on your directory structure

function getDirectCosts($access_token, $company_id, $project_id, $page = 1, $per_page = 100) {
$endpoint = "https://api.procore.com/rest/v1.1/projects/$project_id/direct_costs";
$direct_costs = [];
$n_costs = 1;

while ($n_costs > 0) {
$params = [
"page" => $page,
"per_page" => $per_page
];

$query = http_build_query($params);
$url = $endpoint . '?' . $query;

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $access_token,
'Content-Type: application/json',
'Procore-Company-Id: ' . $company_id
]);

$response = curl_exec($ch);

if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
return null;
}

$costs_per_page = json_decode($response, true);
$n_costs = count($costs_per_page);

$direct_costs = array_merge($direct_costs, $costs_per_page);

$page++;
curl_close($ch);
}

return $direct_costs;
}

// Main execution
$access_token = getAccessToken($client_id, $client_secret);

$company_id = 8089;
$project_id = 2783683;
$direct_costs = getDirectCosts($access_token, $company_id, $project_id);
echo "Direct Costs: \n";
print_r($direct_costs);
?>
Loading

0 comments on commit 0ea9b81

Please sign in to comment.