Skip to content

Commit 862a5e3

Browse files
committed
add python demo
1 parent e2c7893 commit 862a5e3

File tree

6 files changed

+645
-0
lines changed

6 files changed

+645
-0
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Copyright 2018 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# pylint: disable=E1102
16+
# python3
17+
"""Retrieves customer data from our companies "internal" customer data serivce.
18+
19+
This data service is meant to be illustrative for demo purposes, as its just
20+
hard coded data. This can be any internal or on-premise data store.
21+
"""
22+
23+
24+
class CustomerDataService(object):
25+
_CUSTOMER_DATA = {
26+
'mars': {
27+
'customer_name': 'Mars Inc.',
28+
'customer_logo':
29+
'https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/' +
30+
'OSIRIS_Mars_true_color.jpg/550px-OSIRIS_Mars_true_color.jpg',
31+
'curr_q': 'Q2',
32+
'curr_q_total_sales': '$2,532,124',
33+
'curr_q_qoq': '0.054',
34+
'prev_q': 'Q1',
35+
'prev_q_total_sales': '$2,413,584',
36+
'next_q': 'Q3',
37+
'next_q_total_sales_proj': '$2,634,765',
38+
'next_q_qoq_proj': '0.041',
39+
'top1_sku': 'Phobos',
40+
'top1_sales': '$334,384',
41+
'top2_sku': 'Deimos',
42+
'top2_sales': '$315,718',
43+
'top3_sku': 'Charon',
44+
'top3_sales': '$285,727',
45+
'top4_sku': 'Nix',
46+
'top4_sales': '$264,023',
47+
'top5_sku': 'Hydra',
48+
'top5_sales': '$212,361',
49+
},
50+
'jupiter': {
51+
'customer_name': 'Jupiter LLC',
52+
'customer_logo':
53+
'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/' +
54+
'Jupiter_and_its_shrunken_Great_Red_Spot.jpg/660px-Jupiter_' +
55+
'and_its_shrunken_Great_Red_Spot.jpg',
56+
'curr_q': 'Q2',
57+
'curr_q_total_sales': '$1,532,124',
58+
'curr_q_qoq': '0.031',
59+
'prev_q': 'Q1',
60+
'prev_q_total_sales': '$1,413,584',
61+
'next_q': 'Q3',
62+
'next_q_total_sales_proj': '$1,634,765',
63+
'next_q_qoq_proj': '0.021',
64+
'top1_sku': 'Io',
65+
'top1_sales': '$234,384',
66+
'top2_sku': 'Europa',
67+
'top2_sales': '$215,718',
68+
'top3_sku': 'Ganymede',
69+
'top3_sales': '$185,727',
70+
'top4_sku': 'Callisto',
71+
'top4_sales': '$164,023',
72+
'top5_sku': 'Amalthea',
73+
'top5_sales': '$112,361',
74+
},
75+
'saturn': {
76+
'customer_name': 'Saturn',
77+
'customer_logo':
78+
'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/' +
79+
'Saturn_during_Equinox.jpg/800px-Saturn_during_Equinox.jpg',
80+
'curr_q': 'Q2',
81+
'curr_q_total_sales': '$2,532,124',
82+
'curr_q_qoq': '0.032',
83+
'prev_q': 'Q1',
84+
'prev_q_total_sales': '$2,413,584',
85+
'next_q': 'Q3',
86+
'next_q_total_sales_proj': '$2,634,765',
87+
'next_q_qoq_proj': '0.029',
88+
'top1_sku': 'Mimas',
89+
'top1_sales': '$334,384',
90+
'top2_sku': 'Enceladus',
91+
'top2_sales': '$315,718',
92+
'top3_sku': 'Tethys',
93+
'top3_sales': '$285,727',
94+
'top4_sku': 'Dione',
95+
'top4_sales': '$264,023',
96+
'top5_sku': 'Rhea',
97+
'top5_sales': '$212,361',
98+
},
99+
'neptune': {
100+
'customer_name': 'Neptune',
101+
'customer_logo':
102+
'https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/' +
103+
'Neptune_Full.jpg/600px-Neptune_Full.jpg',
104+
'curr_q': 'Q2',
105+
'curr_q_total_sales': '$2,532,124',
106+
'curr_q_qoq': '0.027',
107+
'prev_q': 'Q1',
108+
'prev_q_total_sales': '$2,413,584',
109+
'next_q': 'Q3',
110+
'next_q_total_sales_proj': '$2,634,765',
111+
'next_q_qoq_proj': '0.039',
112+
'top1_sku': 'Triton',
113+
'top1_sales': '$334,384',
114+
'top2_sku': 'Nereid',
115+
'top2_sales': '$315,718',
116+
'top3_sku': 'Naiad',
117+
'top3_sales': '$285,727',
118+
'top4_sku': 'Thalassa',
119+
'top4_sales': '$264,023',
120+
'top5_sku': 'Despina',
121+
'top5_sales': '$212,361',
122+
},
123+
}
124+
125+
def GetCustomerData(self, customer_id, properties):
126+
customer_data = self._CUSTOMER_DATA[customer_id]
127+
return [customer_data[p.lower()] for p in properties]
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Copyright 2018 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# pylint: disable=E1102
16+
# python3
17+
"""Reads the customer data from the template spreadsheet."""
18+
19+
import collections
20+
21+
22+
class CustomerSpreadsheetReader(object):
23+
24+
def __init__(self, sheets_service, spreadsheet_id):
25+
self._sheets_service = sheets_service
26+
self._spreadsheet_id = spreadsheet_id
27+
self._data_filters = collections.OrderedDict()
28+
29+
def ReadColumnData(self, column_id):
30+
data_filter = {
31+
'developerMetadataLookup': {
32+
'metadataKey': 'column_id',
33+
'metadataValue': column_id,
34+
}
35+
}
36+
self._data_filters[column_id] = data_filter
37+
38+
def ExecuteRead(self):
39+
filters = list(self._data_filters.values())
40+
get_body = {'dataFilters': filters}
41+
read_fields = ('sheets.properties.sheetId,sheets.data.rowData.' +
42+
'values.formattedValue,developerMetadata.metadataValue')
43+
spreadsheet = self._sheets_service.spreadsheets().getByDataFilter(
44+
spreadsheetId=self._spreadsheet_id, body=get_body,
45+
fields=read_fields).execute()
46+
customer_spreadsheet = CustomerSpreadsheet(
47+
spreadsheet, self._data_filters)
48+
self._data_filters = collections.OrderedDict()
49+
return customer_spreadsheet
50+
51+
52+
class CustomerSpreadsheet(object):
53+
54+
def __init__(self, spreadsheet, data_filters):
55+
self._spreadsheet = spreadsheet
56+
self._data_filters = data_filters
57+
58+
def GetSheetId(self):
59+
sheet = self._spreadsheet.get('sheets')[0]
60+
return sheet.get('properties').get('sheetId')
61+
62+
def GetTemplateId(self):
63+
metadata = self._spreadsheet.get('developerMetadata')[0]
64+
return metadata.get('metadataValue')
65+
66+
def GetColumnData(self, column_id):
67+
index = list(self._data_filters.keys()).index(column_id)
68+
data = self._spreadsheet.get('sheets')[0].get('data')[index]
69+
values = []
70+
for row in data.get('rowData'):
71+
value = row.get('values')[0].get('formattedValue')
72+
values.append(value)
73+
# Remove the first value which is just the label
74+
return values[1:]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Copyright 2018 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# pylint: disable=E1102
16+
# python3
17+
"""Reads presentation data.
18+
19+
Retrieves a presentation and extracts the placeholders and the presentation's
20+
title.
21+
"""
22+
import re
23+
24+
25+
class PresentationReader(object):
26+
27+
def __init__(self, slides_service, presentation_id):
28+
self._slides_service = slides_service
29+
self._presentation_id = presentation_id
30+
self._presentation = None
31+
32+
def _InitPresentation(self):
33+
if not self._presentation:
34+
self._presentation = self._slides_service.presentations().get(
35+
presentationId=self._presentation_id).execute()
36+
37+
def GetTitle(self):
38+
self._InitPresentation()
39+
return self._presentation.get('title')
40+
41+
def GetAllPlaceholders(self):
42+
self._InitPresentation()
43+
slides = self._presentation.get('slides')
44+
placeholders = []
45+
for slide in slides:
46+
elements = slide.get('pageElements')
47+
for element in elements:
48+
shape = element.get('shape')
49+
table = element.get('table')
50+
# Skip page elements that aren't shapes or tables since they're
51+
# the only types that support text.
52+
if not shape and not table:
53+
continue
54+
if shape:
55+
placeholders += self._GetPlaceholdersFromText(
56+
shape.get('text'))
57+
elif table:
58+
rows = table.get('tableRows')
59+
for row in rows:
60+
cells = row.get('tableCells')
61+
for cell in cells:
62+
placeholders += self._GetPlaceholdersFromText(
63+
cell.get('text'))
64+
# Return the unique placeholders
65+
seen = set()
66+
return [p for p in placeholders if not (p in seen or seen.add(p))]
67+
68+
def _GetPlaceholdersFromText(self, text):
69+
if not text:
70+
return []
71+
placeholders = []
72+
elements = text.get('textElements')
73+
for element in elements:
74+
if element.get('textRun'):
75+
content = element.get('textRun').get('content')
76+
placeholders += re.findall('{.*?}', content)
77+
return placeholders
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright 2018 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# pylint: disable=E1102
16+
# python3
17+
"""Functionality for writing to a presentation.
18+
"""
19+
20+
21+
class PresentationWriter(object):
22+
"""Queues writes for modifying a presentation.
23+
24+
Call ExecuteBatchUpdate to flush pending writes.
25+
"""
26+
27+
def __init__(self, slides_service, presentation_id):
28+
self._slides_service = slides_service
29+
self._presentation_id = presentation_id
30+
self._requests = []
31+
32+
def ReplaceAllText(self, find_text, replace_text):
33+
request = {
34+
'replaceAllText': {
35+
'replaceText': replace_text,
36+
'containsText': {
37+
'text': find_text,
38+
'matchCase': True
39+
}
40+
}
41+
}
42+
self._requests.append(request)
43+
44+
def ReplaceAllShapesWithImage(self, find_text, image_url):
45+
request = {
46+
'replaceAllShapesWithImage': {
47+
'imageUrl': image_url,
48+
'containsText': {
49+
'text': find_text,
50+
'matchCase': True
51+
}
52+
}
53+
}
54+
self._requests.append(request)
55+
56+
def ExecuteBatchUpdate(self):
57+
body = {'requests': self._requests}
58+
self._requests = []
59+
self._slides_service.presentations().batchUpdate(
60+
presentationId=self._presentation_id, body=body).execute()

0 commit comments

Comments
 (0)