- Enable
TimeSpan
aspect forProject
underCoreAdmin
->Configuration
->Aspect Info
. - Create
TimeSpan
dynatype based onProject
base type underCoreAdmin
->Configuration
->Dynatypes
(useproject_status
from drop down as status list). - Create
TimeSpanProject
project template template underCoreAdmin
->Templates
. - Remember template id for further usage in examples
- Ensure that
Contract
dynatype based onFile
is geristered and required aspects containContractDate
andLetterOut
(underCoreAdmin
->Configuration
->Dynatypes
).
Coredata administrator may use following code snippet to get or generate key api via repl
:
from core2.auth.models import ApiKey
from django.contrib.auth.models import User
def get_or_create_apikey(username):
key = ApiKey.objects.filter(user__username=username).first()
if key:
return key.key
else:
user = User.objects.filter(username=username).all()[0]
key = ApiKey(user=user)
key.key = key.generate_key()
key.save()
return key.key
api_username = 'Administrator'
api_key = get_or_create_apikey(api_username)
print 'api key for %s: %s' % (api_username, api_key)
- Find required space id under
CoreAdmin
->Spaces
. - Find required fileplan id under
CoreAdmin
->Fileplans
.
Example below uses V2 api to perform following tasks:
- Creates new project using TimeSpan project template and fills in both fileplan and specific fields like
all_day
andstart_time
. - Uploads Contract file into project and fills in specific fields (
authors
,date
,valid_from
andvalid_to
). - Updates created project with new status, description and end time.
import requests
import urlparse
from django.utils.http import urlquote_plus
from datetime import datetime, timedelta
#####################################################################
# Base api support classes
#####################################################################
def create_apikey_v2_authenticator(username, key):
"""
Apikey authentication for v2 api passes both username and key
"""
def func():
return 'apikey %s:%s' % (username, key)
return func
class CoreDataConnectionError(Exception):
pass
class CoreDataApi(object):
def __init__(self, base_url, authenticator):
self.base_url = base_url
self.authenticator = authenticator
self._session = requests.Session()
def execute_request(
self, method, url, json=None, data=None, files=None, headers=None,
application='coredata'
):
"""
:param method: Request method.
:param url: Relative URL to make request to.
:param json: Dictionary to be json-ified to send as request body.
:param files: Dictionary of file-like-objects for multipart encoding
upload.
:param headers: Dictionary of headers to be parsed with the request
:return: Response object, json or location.
"""
headers = headers or {
'content-type': 'application/json',
}
headers.update({'Authorization': self.authenticator()})
url = self.base_url + url
response = None
try:
response = self._session.request(
method, url, json=json, data=data, files=files,
headers=headers, allow_redirects=False)
response.raise_for_status()
except (requests.ConnectionError, requests.HTTPError) as e:
if response is not None:
raise CoreDataConnectionError(response.content)
else:
raise CoreDataConnectionError(e)
return response
class CoreDataV2(object):
def __init__(self, base_url, authenticator):
base_url = urlparse.urljoin(base_url, '/api/v2')
self.coredata_api = CoreDataApi(base_url, authenticator)
def create_project(self, json):
response = self.coredata_api.execute_request(
'POST', '/projects/', json=json)
# Location header contains path to the project:
# /api/v2/projects/947a6c82-507b-11e9-aa3e-df012f56c42f/
location = response.headers['Location']
return location[17:-1]
def update_project(self, project_id, json):
url = '/projects/{}/'.format(project_id)
self.coredata_api.execute_request('PUT', url, json=json)
def get_projects(self):
return self.coredata_api.execute_request('GET', '/projects/').json()
def get_project(self, project_id):
url = '/projects/{}/'.format(project_id)
return self.coredata_api.execute_request('GET', url).json()
def create_file(self, data, content):
headers = {
'Content-Disposition': u'attachment; filename={}'.format(
urlquote_plus(data['filename'])
),
}
content.seek(0)
response = self.coredata_api.execute_request(
'POST',
'/files/',
headers=headers,
data=data,
files={
'content': (data['filename'], content)
}
)
# Location header contains path to the project:
# /api/v2/files/947a6c82-507b-11e9-aa3e-df012f56c42f/
location = response.headers['Location']
return location[14:-1]
def update_file(self, file_id, json):
url = '/files/{}/'.format(file_id)
self.coredata_api.execute_request('PUT', url, json=json)
def get_spaces(self):
return self.coredata_api.execute_request('GET', '/spaces/').json()
def get_space(self, space_id):
url = '/spaces/{}/'.format(space_id)
return self.coredata_api.execute_request('GET', url).json()
def get_space_projects(self, space_id):
url = '/spaces/{}/projects/'.format(space_id)
return self.coredata_api.execute_request('GET', url).json()
def get_space_project_templates(self, space_id):
url = '/spaces/{}/projects/templates/'.format(space_id)
return self.coredata_api.execute_request('GET', url).json()
def get_space_fileplans(self, space_id):
"""
Returns list of dictionaries with attributes: 'id', 'title'
"""
url = '/spaces/{}/fileplans/'.format(space_id)
return self.coredata_api.execute_request('GET', url).json()
#####################################################################
# Example functions
#####################################################################
def create_new_timespan_project(
coredata, template_id, space_id, fileplan_id, responsible_user):
"""
Creates new project using TimeSpan template and populates timespan aspect fields.
Please note how start_time/end_time fields are populated.
"""
start_datetime = datetime.now() - timedelta(hours=1)
start_date = start_datetime.strftime('%d-%m-%Y')
start_time = start_datetime.strftime('%H:%M')
json = {
'description': 'This is sample project',
'space': space_id,
'title': 'Sample Project (TimeSpan)',
'fileplan': fileplan_id,
'responsible_users': [responsible_user],
'template': template_id,
'timespan__all_day': 'false',
'timespan__start_time': [start_date, start_time],
}
created_project_id = coredata.create_project(json)
return created_project_id
def upload_contract_file(coredata, project_id, filename, author):
"""
Creates Contract file in coredata by specifying dynatype.
Api v2 does not support creating files with dynatype and setting aspect
values in one request.
That's why Contract file is created first and only after that aspect
values are updated.
"""
with open(filename, 'rb') as file:
created_file_id = coredata.create_file(
data={
'filename': filename,
'project': project_id,
'title': 'Contract for %s' % filename,
'dynatype': 'Contract',
},
content=file
)
now = datetime.now().strftime('%Y-%m-%d')
yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
month = (datetime.now() + timedelta(days=30)).strftime('%Y-%m-%d')
aspect_data = {
'letterout__authors': [author],
'letterout__date': now,
'contractdate__valid_from': yesterday,
'contractdate__valid_to': month,
}
coredata.update_file(created_file_id, aspect_data)
return created_file_id
def update_project(coredata, project_id, status):
"""
Updates project description, title, status and end time
Aspects values are fully replaced => previous values are required
"""
project = coredata.get_project(project_id)
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
now_date = datetime.now().strftime('%d-%m-%Y')
now_time = datetime.now().strftime('%H:%M')
title = project['title']
timespan__all_day = str(project['aspects']['timespan']['all_day']).lower()
timespan__start_time = datetime.strptime(
project['aspects']['timespan']['start_time'],
'%Y-%m-%dT%H:%M:%S')
json = {
'description': 'This project was updated at %s. More info: http://google.com' % now,
'title': '%s [%s]' % (title, now),
'status': status,
'timespan__all_day': timespan__all_day,
'timespan__start_time': [timespan__start_time.strftime('%d-%m-%Y'),
timespan__start_time.strftime('%H:%M')],
'timespan__end_time': [now_date, now_time],
}
coredata.update_project(project_id, json)
#####################################################################
# V2 Example
#####################################################################
# External configuration parameters
api_username = 'Administrator'
api_key = '4fb69e4a8a39c1560c590da482373c732ba874b6'
space_id = 'e4dc5228-8b66-11e9-a2fd-9f4406f7b89f'
timespan_project_template_id = 'b33a7644-8b7c-11e9-8f19-c72291ab1e6b'
fileplan_id = 'be7f2472-8b6e-11e9-9d08-9f1e292e30b1'
# project status format is: '{status_list}:{status_id}:'
updated_project_status = 'project_status:In_progress:'
coredata_v2 = CoreDataV2(
"http://127.0.0.1:8100",
create_apikey_v2_authenticator(api_username, api_key))
print '*** Running sample project creation and file uploading'
print 'creating new timespan project...'
project_id = create_new_timespan_project(
coredata_v2, timespan_project_template_id, space_id, fileplan_id,
api_username)
print ' timespan project created. id: %s' % project_id
print 'uploading new contract...'
contract_id = upload_contract_file(
coredata_v2, project_id, 'userlist.txt', api_username)
print ' new contract uploaded. id: %s' % contract_id
print 'updating project...'
update_project(coredata_v2, project_id, updated_project_status)
print ' project updated'
- Cannot set aspect fields during creation call; needs additional document update call to set aspect fields (see
upload_contract_file
method); - Field format follows format defined by html forms (see how timespan
end_time
/start_time
fields are populated in timespan project creation increate_new_timespan_project
method); - Returned field values representation is not compatible with creation expected format (see how
start_time
is parsed/formatted inupdate_project
method)