Архив метки: 2.7

Скачивание файла с 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
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
# .
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&#
39;)
  # Так как файл в родом формате для гугла, то скачать его нельзя и
  # 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 '%s' %match.group(2) * int(match.group(1)) 
  def repl_empt(match): 
    n = int(match.group(1)) 
    pat = '
    return pat*n if (n<100) else pat 
     
  p_repl = re.compile(r']*?repeated=»(d+)[^/>]*>(.+?table-cell>)') 
  p_empt = re.compile(r']*?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:pen(''.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)

Автор: Ishayahu Lastov

object.__del__(self) 2.7

Вызывается когда экземпляр должен быть уничтожен (другими словами — это деструктор). Если родительский класс тоже имеет метод __del__(), то производный класс в своём методе __del__(), если он определён, должен явно вызывать метод родительского класса, чтобы гарантированно уничтожить методы родительского класса. Стоит отметить, что возможно (хотя и не рекомендуется) сделать так, чтобы в методе __del__() было отложено уничтожение самого объекта. Это достигается созданием на него другой ссылки перед удалением текущей, и уже при уничтожении последней ссылки надо будет уничтожить сам объект. Гарантии того, что метод __del__() будет вызван для существующих объектов при завершении работы интерпретатора нет.
Заметка
del x не является прямым вызовом x.__del__() — первая форма сокращает количество ссылок на объект x на одну, тогда как последний метод вызывается только когда количество ссылок достигает нуля. В некоторых часто встречающихся случаях могут возникнуть ситуации, мешающие обнулению счётчика, как то:
  1. взаимные ссылки между объектами (в списках или в деревьях)
  2. ссылки на объекты в стеке функции, где было вызвано исключение, так как в таком случае ссылки на объекты этого стека сохранены в sys.exc_traceback
  3. ссылки на объекты в стеке, если было вызвано не перехваченное исключение в интерактивном режиме (так как в таком случае ссылки на объекты сохранены в sys.last_traceback)
В первом случае необходимо явно разрушить циклические ссылки; во втором и третьем — сохранить None в sys.exc_traceback или sys.last_traceback. Циклические ссылки определяются сборщиком мусора, если активирована соответствующая опция (как оно и есть по умолчанию), однако, если вызывается метод __del__() в коде, такие ссылки не будут обработаны автоматически. Обратитесь к документации модуля gc для более подробной информации, особенно к разделу, описывающему значение garbage.
Предупреждение
В связи с неопределёнными обстоятельствами, когда вызывается метод __del__(), исключения, возникающие в процессе выполнения этого метода игнорируются, а предупреждения об этом выводятся в sys.stderr. Кроме того, когда вызывается метод __del__(), относящийся к удалению модуля (например, когда завершено выполнение программы) другие объекты, определённые в этом методе могут быть уже уничтожены или быть в процессе уничтожения (например, когда происходит выключение механизма импортирования). По этой причине метод __del__() должен содержать минимум внешних зависимостей. Начиная с версии 1.5 Python гарантирует, что глобальные имена, начинающиеся с _ удаляются из модуля прежде остальных глобальных имён; поэтому если нет других ссылок на эти переменные, по их наличию можно определить доступность  импортированного модуля в процессе вызова метода __del__().

Автор: Ishayahu Lastov