· 5 years ago · Mar 12, 2021, 06:12 PM
1import io
2import json
3import time
4from datetime import date
5
6import googleapiclient.http as client_methods
7from googleapiclient import discovery
8from httplib2 import Http
9from oauth2client import file, client, tools
10from typing import List
11
12
13class SlidesSnippets:
14 def __init__(self):
15 self.sheets_service = None
16 self.SCOPES = (
17 'https://www.googleapis.com/auth/drive',
18 'https://www.googleapis.com/auth/presentations',
19 )
20 self.store = file.Storage('storage.json')
21 self.creds = self.store.get()
22 if not self.creds or self.creds.invalid:
23 self.flow = client.flow_from_clientsecrets('slides_credentials.json', self.SCOPES)
24 self.creds = tools.run_flow(self.flow, self.store)
25 self.HTTP = self.creds.authorize(Http())
26 self.drive_service = discovery.build('drive', 'v3', http=self.HTTP)
27 self.service = discovery.build('slides', 'v1', http=self.HTTP)
28 self.representante = {'Eduardo': '1ZuHbmcWxrvz66_DYjooPS2lyZprE2YgjD9jjz4iPgu4'}
29
30 # ter funcao que receba float e retorne str formatada com R$ (divisao de milhar tambem)
31 @staticmethod
32 def currency_format(value: float) -> str:
33 """ Receive a float 1000.00 or 1000.0 and returns R$ 1.000,00 """
34 value = str('%.2f' % value).replace('.', ',')
35 splited_value = value.split(',')
36 if len(splited_value[0]) >= 4:
37 value = splited_value[0][:-3] + '.' + splited_value[0][-3:] + ',' + splited_value[1]
38 return 'R$ ' + value
39
40 # ter funcao para incluir representante
41 def add_new_representative(self, name, slide_id):
42 """ add new trade representative"""
43 self.representante[name] = slide_id
44
45 # ter funcao para remover representante
46 def del_representative(self, name):
47 """ remove trade representative"""
48 self.representante.pop(name)
49
50 # lisa representantes # TODO
51 def list_representative(self):
52 return [key for key in self.representante.keys()]
53
54 # criar funcao que verifique se existe pasta com o nome do representante, caso contrario, cria-la
55 def search_representative_folder(self, representative_name: str):
56 """ Search for a folder with the given name """
57 if representative_name not in self.representante:
58 return False
59
60 found = False
61 folder_name = None
62 response = self.drive_service.files().list(
63 q="mimeType='application/vnd.google-apps.folder' and trashed=False").execute()
64 for folder in response.get('files'):
65 if folder.get('name') == representative_name:
66 print('Pasta "%s" encontrada com ID %s' % (folder.get('name'), folder.get('id')))
67 found = True
68 folder_name = folder.get('id')
69 if not found:
70 return self.create_representatives_folder(representative_name)
71 else:
72 return folder_name
73
74 # criar funcao que crie uma pasta com o nome do representante
75 def create_representatives_folder(self, representative_name: str):
76 file_metadata = {
77 'name': representative_name,
78 'mimeType': 'application/vnd.google-apps.folder'
79 }
80 folder = self.drive_service.files().create(body=file_metadata).execute()
81 print(f'Pasta do representante {representative_name} criada.')
82 return folder.get('id')
83
84 # criar funcao que crie uma pasta com o nome do cliente
85 def create_client_folder(self, client_name: str, id_representative_folder):
86 file_metadata = {
87 'name': client_name,
88 'mimeType': 'application/vnd.google-apps.folder',
89 'parents': [id_representative_folder]
90 }
91 folder = self.drive_service.files().create(body=file_metadata).execute()
92 print(f'Pasta do cliente {client_name} criada.')
93 return folder.get('id')
94
95 # cria pasta da proposta com nome do cliente e id da negociacao
96 def create_proposal_folder_with_file(self, proposal_name: str, id_representative_folder: str):
97 file_metadata = {
98 'name': proposal_name,
99 'mimeType': 'application/vnd.google-apps.folder',
100 'parents': [id_representative_folder]
101 }
102 folder = self.drive_service.files().create(body=file_metadata, ).execute()
103 print(f'Pasta da proposta {proposal_name} criada.')
104 return folder.get('id')
105
106 # move a proposta para a pasta correta
107 def move_proposal_to_representative_folder(self, id_file, id_folder):
108 file_id = id_file
109 folder_id = id_folder
110 # Retrieve the existing parents to remove
111 file = self.drive_service.files().get(fileId=file_id, fields='parents').execute()
112 previous_parents = ",".join(file.get('parents'))
113 # Move the file to the new folder
114 file = self.drive_service.files().update(fileId=file_id,
115 addParents=folder_id,
116 removeParents=previous_parents,
117 fields='id, parents').execute()
118 print('Arquivo movido para a pasta')
119 return file.get('id')
120
121 # verifica existencia de pastas ou arquivos
122 def search_file_and_folder(self, required_name: str, required_id: str, func: callable):
123 found = False
124 item_found = None
125 dict_params = {
126 "q": f"'{required_id}' in parents",
127 "fields": "nextPageToken,incompleteSearch,files(id,parents,name)",
128 }
129 request = self.drive_service.files().list(**dict_params)
130 while request is not None:
131 response = request.execute()
132 for item in response.get('files', []):
133 if item['name'] == required_name:
134 print('Pasta: %s encontrada com Id: %s' % (item['name'], item['id']))
135 found = True
136 item_found = item['id']
137 request = self.drive_service.files().list_next(request, response)
138
139 if not found:
140 try:
141 return func(required_name, required_id)
142 except TypeError:
143 return False
144 else:
145 return item_found
146
147 # cria a copia do template para ser preenchido
148 def copy_presentation(self, presentation_id, copy_title):
149 # [START slides_copy_presentation]
150 body = {
151 'name': copy_title
152 }
153 drive_response = self.drive_service.files().copy(fileId=presentation_id, body=body).execute()
154 presentation_copy_id = drive_response.get('id')
155 # [END slides_copy_presentation]
156 print('Criado slide com ID: {0}'.format(presentation_copy_id))
157 return presentation_copy_id
158
159 # efetua o replace dos textos desejados
160 def simple_text_replace(self, presentation_id, requests_to_replace):
161 slides_service = self.service
162 # [START slides_simple_text_replace]
163
164 # Execute the requests.
165 body = {
166 'requests': requests_to_replace,
167 }
168
169 response = slides_service.presentations().batchUpdate(presentationId=presentation_id, body=body).execute()
170 print('Textos trocados no slide com ID: {0}'.format(presentation_id))
171 requests_to_replace.clear()
172 # [END slides_simple_text_replace]
173 return response
174
175 # converte o arquivo em pdf
176 def convert_to_pdf(self, id_folder, id_file, title):
177 # exporting the slide deck and specifying the desired final file type
178 data = self.drive_service.files().export(fileId=id_file, mimeType='application/pdf').execute()
179
180 # request body to be send together the upload method
181 body = {'name': title, 'mimeType': 'application/pdf'}
182
183 # wrapping the binary (data) file with BytesIO class
184 fh = client_methods.BytesIO(data)
185
186 # creating the Media Io upload class for the file (note that our original slide data is of binary type)
187 media_body = client_methods.MediaIoBaseUpload(fh, mimetype='application/pdf')
188
189 # drive API v3 - .create | drive API v2 - .insert
190 pdf_file_id = self.drive_service.files().create(body=body, media_body=media_body).execute()['id']
191 print('ID do PDF: ', pdf_file_id)
192
193 # extra step: moving to desirable folder destination with the function method
194
195 def converting_to_pdf(drive_client, id_, folder_id):
196 file_ = drive_client.files().get(fileId=id_, fields='parents').execute()
197 drive_client.files().update(fileId=id_, addParents=folder_id,
198 removeParents=file_['parents'][0],
199 fields='id,parents').execute()
200
201 # folder_id = id from url
202 self.download_pdf(title, id_file)
203 converting_to_pdf(self.drive_service, pdf_file_id, id_folder)
204 return pdf_file_id
205
206 # def baixar_pdf(id_pdf):
207 def download_pdf(self, file_name, id_file):
208 """ Receive ID file and download it """
209 file_id = id_file
210 request = self.drive_service.files().export_media(fileId=file_id, mimeType='application/pdf')
211 fh = io.FileIO(file_name, 'wb')
212 downloader = client_methods.MediaIoBaseDownload(fh, request, chunksize=1024 * 1024)
213 done = False
214 while done is False:
215 _, done = downloader.next_chunk()
216 print("Download concluido")
217
218 def removing_ppt(self, ppt_id):
219 """ removing ppt """
220 self.drive_service.files().delete(fileId=ppt_id).execute()
221 print('Arquivo removido com sucesso!')
222
223 def empty_trash(self):
224 self.drive_service.files().emptyTrash().execute()
225 time.sleep(3)
226 print('Lixeira esvaziada!')
227
228 def main(self, client_name: str, folder_name: str, proposal_id: str, representative: str, amount_list: List[int], monthly_value_list: List[float],
229 provisioning_value_list: List[float], presentation_id='ID DO TEMPLATE PPT',
230 _date=date.today().strftime('%d/%m/%Y')): # TODO retirar presentation_id padrao
231
232 file_name = f'{proposal_id} - Proposta Comercial - {client_name} - {amount_list[0]} Gbps Mitigacao - ' \
233 f'{amount_list[1]} Mbps US - {amount_list[2]} Mbps BR.pdf'
234
235 # verifica se ja existe pasta com o nome do representante. Caso contrario cria.
236 representative_folder_id = self.search_representative_folder(representative)
237
238 # verifica se ja existe pasta com o nome do cliente. Caso contrario cria
239 # client_folder_id = self.search_client_folder(client_name, representative_folder_id)
240 client_folder_id = self.search_file_and_folder(client_name, representative_folder_id, self.create_client_folder)
241
242 # verifica se ja existe pasta com o nome da proposta. Caso contrario cria.
243 # proposal_folder_id = self.search_proposal_folder_name(folder_name, client_folder_id)
244 proposal_folder_id = self.search_file_and_folder(folder_name, client_folder_id,
245 self.create_proposal_folder_with_file)
246
247 # file_id = self.search_proposal_file_name(file_name, proposal_folder_id)
248 file_id = self.search_file_and_folder(file_name, proposal_folder_id, False)
249 # Se arquivo nao existe (False), continue o processo de criacao
250 if not file_id:
251 # chamando o metodo que faz a copia do template e salvando o ID da nova copia na variavel
252 new_copy = slides_init.copy_presentation(presentation_id, file_name)
253
254 # template do comando para alteracao dos textos
255 dict_template = {'replaceAllText':
256 {'containsText': {'text': 'text_to_be_find'}, 'replaceText': 'text_to_be_replaced'},
257 }
258
259 # dicionario que contendo os valores corretos para o dict_template
260 templates = {
261 '{{EMPRESA}}': client_name, '{{representante}}': representative, '{{DATA}}': _date,
262 '{{gbps_qtd}}': str(amount_list[0]),
263 '{{gbps_val_mes}}': self.currency_format(monthly_value_list[0]) if amount_list[0] != 0 else '-',
264 '{{gbps_val_prov}}': self.currency_format(provisioning_value_list[0]) if amount_list[0] != 0 else '-',
265 '{{trafego_us_qtd}}': str(amount_list[1]),
266 '{{trafego_us_val_mes}}': self.currency_format(monthly_value_list[1]) if amount_list[1] != 0 else '-',
267 '{{trafego_us_val_prov}}': self.currency_format(provisioning_value_list[1]) if amount_list[1] != 0 else '-',
268 '{{trafego_br_qtd}}': str(amount_list[2]),
269 '{{trafego_br_val_mes}}': self.currency_format(monthly_value_list[2]) if amount_list[2] != 0 else '-',
270 '{{trafego_br_val_prov}}': self.currency_format(provisioning_value_list[2]) if amount_list[2] != 0 else '-',
271 '{{asn_ad_qtd}}': str(amount_list[3]),
272 '{{asn_ad_val_mes}}': self.currency_format(monthly_value_list[3]) if amount_list[3] != 0 else '-',
273 '{{asn_ad_val_prov}}': self.currency_format(provisioning_value_list[3]) if amount_list[3] != 0 else '-',
274 '{{roteador_qtd}}': str(amount_list[4]),
275 '{{roteador_val_mes}}': self.currency_format(monthly_value_list[4]) if amount_list[4] != 0 else '-',
276 '{{roteador_val_prov}}': self.currency_format(provisioning_value_list[4]) if amount_list[4] != 0 else '-',
277 '{{total_mes}}': self.currency_format(sum(monthly_value_list)) if sum(monthly_value_list) != 0 else '-',
278 '{{total_prov}}': self.currency_format(sum(provisioning_value_list)) if sum(
279 provisioning_value_list) != 0 else '-'
280 }
281
282 # lista que guardara os novos valores
283 requests = []
284
285 # criando um novo dict para cada valor a ser substituido e inserindo na lista
286 for key, value in templates.items():
287 dict_template['replaceAllText']['containsText']['text'] = key
288 dict_template['replaceAllText']['replaceText'] = value
289 dict_replace = json.dumps(dict_template)
290 requests.append(json.loads(dict_replace))
291
292 # preenchendo copia criada anteriormente com as informacoes passadas ao requests
293 self.simple_text_replace(new_copy, requests)
294
295 # ID da minha pasta no Drive onde esta o template
296 folder_id = 'ID DA PASTA ONDE ESTÁ O TEMPLATE PPT' # TODO retirar padrao
297
298 # convertendo o ppt para pdf
299 new_pdf_id = self.convert_to_pdf(id_folder=folder_id, id_file=new_copy, title=file_name)
300
301 self.move_proposal_to_representative_folder(new_pdf_id, proposal_folder_id)
302 self.removing_ppt(new_copy)
303 else:
304 print('Proposta existente. Saindo do programa')
305
306
307if __name__ == '__main__':
308 slides_init = SlidesSnippets()
309
310 # esvaziando a lixeira para evitar encontrar arquivos com nomes iguais aos que devem ser criados
311 slides_init.empty_trash()
312
313 slides_init.main(client_name='NOME DO CLIENTE', folder_name='NOME DA PASTA DA NEGOCIACAO', proposal_id='ID DA PROPOSTA', representative='NOME DO REPRESENTANTE',
314 amount_list=[0, 0, 0, 0, 0], provisioning_value_list=[0, 0, 0, 0, 0],
315 monthly_value_list=[0, 0, 0, 0, 0],
316 presentation_id='ID DO PPT TEMPLATE', _date='DATA DA CRIACAO DA PROPOSTA (NAO PASSAR ESTE PARAMETRO RETORNARÁ A DATA ATUAL')
317