воскресенье, 12 мая 2013 г.

Скачивание файла с Google Docs и преобразование ods в csv

Возникла передо мной такая задача: мы храним таблицу соответсвия мака, ip и имени ПК в гуглодоксе, она и поддерживается в актуальном состоянии. DHCP сервер работает на FreeBSD, соответственно файл с настройками сервера вполне себе текстовый. И чтобы не обновлять данные и там и там был сделан скриптик. Наверняка можно было бы сделать проще, в некоторых случаях даже понятно как (например в функции csv2dhcp), но для небольшой таблицы я заморачиваться не стал.
Для работы понадобится pyquery и второй питон (считаем, что это у Вас уже есть). Кроме того, нам нужна библиотека для доступа к Google API.

Итак, по порядку:
  1. Заходим на страницу
  2. Выбираем Drive API и Python/Command Line
  3. Нажимаем на Настроить проект
  4. Вводим имя проекта, Далее
  5. По двум появившимся ссылкам скачиваем starter application и client secret, которым заменяем такой же файл в распакованном архиве
  6. Редактируем файл sample.py
Ниже будет код. Функцию csv2dhcp писал сам, ods2csv взял отсюда, остальное из документации по Google API. Немножно моих комментариев в коде.
Если будут комментарии/улучшения - буду благодарен

# -*- coding: utf-8 -*-
#
# Copyright (C) 2012 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""Command-line skeleton application for Drive API.
Usage:
  $ python sample.py

You can also get help on all the command-line flags the program understands
by running:

  $ python sample.py --help

To get detailed log output run:

  $ python sample.py --logging_level=DEBUG
 
Для работы надо поставить gdata:
 
  #$ pip install gdata
  $ pip install pyquery
 
Подключение API:

  https://developers.google.com/drive/quickstart-python
"""

import gflags
import httplib2
import logging
import os
import sys

from apiclient.discovery import build
from oauth2client.file import Storage
from oauth2client.client import AccessTokenRefreshError
from oauth2client.client import flow_from_clientsecrets
from oauth2client.tools import run
from apiclient import errors

FLAGS = gflags.FLAGS

# CLIENT_SECRETS, name of a file containing the OAuth 2.0 information for this
# application, including client_id and client_secret.
# You can see the Client ID and Client secret on the API Access tab on the
# Google APIs Console <https://code.google.com/apis/console>
CLIENT_SECRETS = 'client_secrets.json'

# Helpful message to display if the CLIENT_SECRETS file is missing.
MISSING_CLIENT_SECRETS_MESSAGE = """
WARNING: Please configure OAuth 2.0

To make this sample run you will need to download the client_secrets.json file
and save it at:

   %s

""" % os.path.join(os.path.dirname(__file__), CLIENT_SECRETS)
# Временные файлы, куда сохраняется скачаный файл и конвертированый в csv
TEMP_FILE = '/home/ishayahu/IP range.ods'
TEMP_FILE2 = '/home/ishayahu/IP range.csv'

# Имя итогового файла
dhcp_file_name = '/home/ishayahu/dhcp'
# Set up a Flow object to be used for authentication.
# Add one or more of the following scopes. PLEASE ONLY ADD THE SCOPES YOU
# NEED. For more information on using scopes please see
# <https://developers.google.com/+/best-practices>.
FLOW = flow_from_clientsecrets(CLIENT_SECRETS,
    scope=[
      'https://www.googleapis.com/auth/drive',
      'https://www.googleapis.com/auth/drive.apps.readonly',
      'https://www.googleapis.com/auth/drive.metadata.readonly',
      'https://www.googleapis.com/auth/drive.file',
      'https://www.googleapis.com/auth/drive.scripts',
      'https://www.googleapis.com/auth/drive.readonly',
    ],
    message=MISSING_CLIENT_SECRETS_MESSAGE)


# The gflags module makes defining command-line options easy for
# applications. Run this program with the '--help' argument to see
# all the flags that it understands.
gflags.DEFINE_enum('logging_level', 'ERROR',
    ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
    'Set the level of logging detail.')

def retrieve_all_files(service):
  """Retrieve a list of File resources.

  Args:
    service: Drive API service instance.
  Returns:
    List of File resources.
  """
  result = []
  page_token = None
  while True:
    try:
      param = {}
      if page_token:
        param['pageToken'] = page_token
      files = service.files().list(**param).execute()

      result.extend(files['items'])
      page_token = files.get('nextPageToken')
      if not page_token:
        break
    except errors.HttpError, error:
      print 'An error occurred: %s' % error
      break
  return result



def download_file(service, drive_file):
  """Download a file's content.

  Args:
    service: Drive API service instance.
    drive_file: Drive File instance.

  Returns:
    File's content if successful, None otherwise.
  """
  #download_url = drive_file.get('downloadUrl')

  # Так как файл в родом формате для гугла, то скачать его нельзя и
  # downloadUrl просто отсутствует. Поэтому используем ссылки для экспорта
  download_url = drive_file[u'exportLinks'][u'application/x-vnd.oasis.opendocument.spreadsheet']
  if download_url:
    resp, content = service._http.request(download_url)
    if resp.status == 200:
      #print 'Status: %s' % resp
      return content
    else:
      print 'An error occurred: %s' % resp
      return None
  else:
    # The file doesn't have any content stored on Drive.
    return None

def ods2csv(filepath): 
  # комментарии тут: http://python-example.blogspot.ru/2012/10/how-to-convert-ods-to-csv.html
  import sys,zipfile,re,os,csv 
  from pyquery import PyQuery as pq 
  from lxml.cssselect import CSSSelector 
     
  xml = zipfile.ZipFile(filepath).read('content.xml') 
   
  def rep_repl(match): 
    return '<table:table-cell>%s' %match.group(2) * int(match.group(1)) 
  def repl_empt(match): 
    n = int(match.group(1)) 
    pat = '<table:table-cell/>' 
    return pat*n if (n<100) else pat 
     
  p_repl = re.compile(r'<table:table-cell [^>]*?repeated="(\d+)[^/>]*>(.+?table-cell>)') 
  p_empt = re.compile(r'<table:table-cell [^>]*?repeated="(\d+)[^>]*>') 
  xml = re.sub(p_repl, rep_repl, xml) 
  xml = re.sub(p_empt, repl_empt, xml) 
     
  d = pq(xml, parser='xml') 
  ns={'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'} 
  selr = CSSSelector('table|table-row', namespaces=ns) 
  selc = CSSSelector('table|table-cell', namespaces=ns) 
  rowxs = pq(selr(d[0])) 
  data = [] 
  for ir,rowx in enumerate(rowxs): 
    cells = pq(selc(rowx)) 
    if cells.text(): 
      data.append([cells.eq(ic).text().encode('utf-8') for ic in range(len(cells))]) 
     
  root,ext=os.path.splitext(filepath) 
  with open(''.join([root,'.csv']),'wb') as f: 
    for row in data: 
      dw = csv.writer(f) 
      dw.writerow(row) 
     
def csv2dhcp(filepath):
  dhcp_file=open(dhcp_file_name,'w')
  for line in open(filepath,'r'):

    if len(line.strip().split(','))>=6 and line.strip().split(',')[2]:
      dhcp_file.write("host %s { hardware ethernet %s; fixed-address 192.168.1.%s; }\n" % (line.strip().split(',')[5],line.strip().split(',')[2],line.strip().split(',')[1]))
  dhcp_file.close()
   
def main(argv):
  # Let the gflags module process the command-line arguments
  try:
    argv = FLAGS(argv)
  except gflags.FlagsError, e:
    print '%s\\nUsage: %s ARGS\\n%s' % (e, argv[0], FLAGS)
    sys.exit(1)

  # Set the logging according to the command-line flag
  logging.getLogger().setLevel(getattr(logging, FLAGS.logging_level))

  # If the Credentials don't exist or are invalid, run through the native
  # client flow. The Storage object will ensure that if successful the good
  # Credentials will get written back to a file.
  storage = Storage('sample.dat')
  credentials = storage.get()

  if credentials is None or credentials.invalid:
    credentials = run(FLOW, storage)

  # Create an httplib2.Http object to handle our HTTP requests and authorize it
  # with our good Credentials.
  http = httplib2.Http()
  http = credentials.authorize(http)

  service = build('drive', 'v2', http=http)

  try:
    print "Login success! Starting to work."
    for entry in retrieve_all_files(service):

    # Пролистываем список всех файлов (как ещё получить нужный не знаю)
    # IP range - имя нужного мне файла
    if entry[u'title']=='IP range':
        print "Writing content to %s" % TEMP_FILE
        open(TEMP_FILE,'w').write(download_file(service, entry))
        print "Converting ods to csv"
        ods2csv(TEMP_FILE)
        csv2dhcp(TEMP_FILE2)
        os.remove(TEMP_FILE)
        os.remove(TEMP_FILE2)
    # For more information on the Drive API API you can visit:
    #
    #   https://developers.google.com/drive/
    #
    # For more information on the Drive API API python library surface you
    # can visit:
    #
    #   https://google-api-client-libraries.appspot.com/documentation/drive/v2/python/latest/
    #
    # For information on the Python Client Library visit:
    #
    #   https://developers.google.com/api-client-library/python/start/get_started

  except AccessTokenRefreshError:
    print ("The credentials have been revoked or expired, please re-run"
      "the application to re-authorize")

if __name__ == '__main__':
  main(sys.argv)

Комментариев нет:

Отправить комментарий