Commits

Anonymous committed e9eea65

Refactor: Changed keys to be just the day (was the entire date) and changed the PDF menu download to download and parse the file in a temporary file (this invalidates the --keep option).

  • Participants
  • Parent commits 7f97d04
  • Tags 2.0b5

Comments (0)

Files changed (2)

 Você pode configurar a execução do **sesc.py** através de opções na linha de comando:
 
 - **-a, --all**: Exibe o cardápio para todos os dias;
-- **-c, --clean**: Limpa o cache antes de buscar o cardápio.
-- **-d, --date=data**: Exibe cardápio para uma data específica (dia, mês e ano, separados por qualquer caractere). Caso o ano e o mês sejam omitidos, será considerado o mês atual. Exemplos de datas válidas: "30", "30 Junho", "30-Jun", "30/06", "30+06+13" ou "30_06_2013" 
-- **-f, --format=formato**:  Altera a formatação da data de acordo com os parâmetros aceitos pela função [time.strftime][strftime] do python. O padrão é "%d/%m/%Y".
+- **-c, --clean**: Limpa o cache antes de buscar o cardápio;
+- **-d, --day=day**: Exibe cardápio para um dia específica;
 - **-g, --gui**: Exibe o cardápio em uma janela. Não é compatível com a opção "-a".
 - **--guser=username**: Configura o nome de usuário do gmail para o envio de e-mail.
 - **--gpass=senha**: Configura a senha de aplicativo do gmail para o envio de e-mail.
 - **-h, --help**: Exibe esta ajuda.
 - **-n, --notify=lista**: Lista de e-mails, separados por ponto e vírgula, a serem notificados do menu.
-- **-k, --keep**: Mantém o arquivo PDF do cardápio, padrão é "False".
 - **-p, --path=caminho**: Caminho onde armazenar os arquivos, padrão "/home/rjfonseca/.sesc".
 - **-q, --quiet**: Modo silencioso. Não exibe mensagens intermediárias.
 - **-u, --unit=unidade**: Unidade do SESC, padrão unidade de "pinheiros". Unidades cadastradas: pinheiros
 	    'pinheiros' : {
 	        'url'     : 'http://www.sescsp.org.br/sesc/busca/index.cfm?unidadesdirector=57#',
 	        'pdf'     : r'(?i).*?card.+?pio.*?',
-	        'years'   : r'^\d{1,2}\s.+\s(\d{2,4})',
-	        'months'  : r'^\d{1,2}\s?(\w+)?\sa\s\d{1,2}\s(\w+)\s\d{2,4}',
 	        'entries' : r'^.?\d\d\s•.+?(?=\n\n)',
 	        'day'     : r'(?P<day>\d\d?)\s',
 	        'menus'   : r'brasileirinho\s*?\n((?:.|\n)+?)\nfog.*?o\scultural\s*\n((?:.|\n)+)'
 
 - **url**: Endereço web da unidade;
 - **pdf**: Expressão regular para extrair o endereço para download do PDF da página da unidade. Essa expressão será usada para comparar com o conteúdo da tag *a*, caso sejá compatível, será extraído o link dessa tag. Em caso de mais de um resultado, será sempre considerado o último;
-- **years**: Expressão regular para extrair os anos aos quais esse cardápios se aplica;
-- **months**: Expressão regular para extrair os meses aos quais esse cardápios se aplica;
 - **entries**:  Expressão regular para extrair as entradas diárias no cardápio;
 - **day**: Expressão regular para extrair o dia específico. É obrigatória a presença de um grupo chamado ```day``` na expressão, do qual será extraído o dia;
 - **menus**: Expressão regular para extrair uma lista com o cardápio do dia;
 from cStringIO import StringIO
 from os.path import expanduser, join
 from HTMLParser import HTMLParser
+from tempfile import TemporaryFile
 import re
 import os
 import time
     'Pinheiros' : {
         'url'     : 'http://www.sescsp.org.br/unidades/unidade-facilidades.action?id=10',
         'pdf'     : r'(?i).*?card.+?pio.*?',
-        'years'   : r'^.+\s(\d{2,4})',
-        'months'  : r'^\d{1,2}[^\s]*\s?(\w+)?\sa\s\d{1,2}[^\s]*\s(\w+)\s\d{2,4}',
         'entries' : r'^.?\d\d\s•.+?(?=\n\n)',
         'day'     : r'(?P<day>\d\d?)\s',
         'menus'   : r'brasileirinho\s*?\n((?:.|\n)+?)\nfog.*?o\scultural\s*\n((?:.|\n)+)'
 # You may edit the default values if you like
 default_unit = 'Pinheiros'
 default_home = join(expanduser("~"),'.sesc')
-default_keep = False
 cache_filename = 'cache.json'
 
 # But do not edit below this point, if you don't know what you are doing
 
 locale.setlocale(locale.LC_ALL, '')
-cache_version = 2
+cache_version = 3
 context = {'version' : cache_version}
 quiet = True
-key_format = '%Y-%m-%d'
-date_format = '%d/%m/%Y'
 
 # Wrap sys.stdout into a StreamWriter to allow writing unicode.
 # Thanks to EOL: http://stackoverflow.com/questions/4545661/unicodedecodeerror-when-redirecting-to-file
         pdf_url = ps[0]+'://'+ps[1]+pdf_url
     return pdf_url
 
-def get_key(timestamp = None):
-    """Generate a key based on a localized timestamp string, e.g. "01 Janeiro 2013", for a menu entry."""
-    if timestamp:
-        date = re.search(r'(?P<day>\d{1,2})(?:(?P<sep>.)(?P<month>\d{1,2}|\w+)(?:(?P=sep)(?P<year>\d{2,4}))?)?', timestamp).groupdict()
-        
-        if not date['sep']:
-            date['sep'] = '-'
-        if not date['year']:
-            date['year'] = time.strftime('%Y')
-        if not date['month']:
-            date['month'] = time.strftime('%m')
+def get_current_day():
+    return time.strftime('%d')
 
-        if re.match(r'^\d?\d$', date['day']):
-            day = '%d'
-        else:
-            raise 'Unkown day format'
+def get_pdf_as_text(url):
+    """ Convert a PDF into a string."""
 
-        if re.match(r'^\d+$', date['month']):
-            month = '%m'
-        elif re.match(r'^\w{3}$', date['month']):
-            month = '%b'
-        elif re.match(r'^\w+$', date['month']):
-            month = '%B'
-        else:
-            raise 'Unkown month format'
-        
-        if re.match(r'^\d\d$', date['year']):
-            year = '%y'
-        elif  re.match(r'^\d{4}$', date['year']):
-            year = '%Y'
-        else:
-            raise 'Unkown year format'
-        
-        format = date['sep'].join([day, month, year])
-        timestamp = date['sep'].join([date['day'], date['month'], date['year']])
+    with TemporaryFile() as fp:
 
-        return time.strftime(key_format, time.strptime(timestamp, format))
-    else:
-        return time.strftime(key_format)
+        req = urllib2.Request(url)
+        r = urllib2.urlopen(req)
 
-def key_to_date(key):
-    return time.strftime(date_format, time.strptime(key, key_format))
+        fp.write(r.read())
+        fp.flush()
 
+        rsrcmgr = PDFResourceManager()
+        retstr = StringIO()
+        device = TextConverter(rsrcmgr, retstr, codec='utf-8', laparams=LAParams())
 
-def get_month_nr(month):
-    if month:
-        if re.match(r'\d+$', month):
-            format = '%m'
-        elif re.match(r'\w{3}$', month):
-            format = '%b'
-        elif re.match(r'\w+$', month):
-            format = '%B'
-        else:
-            raise 'Unkown month format'
-        return time.strftime('%m', time.strptime(month, format))
-    else:
-        return -1
+        process_pdf(rsrcmgr, device, fp)
+        device.close()
 
-def url2name(url):
-    """Extract the file name of a download link."""
-    return basename(urlsplit(url)[2])
+        text = retstr.getvalue()
+        retstr.close()
 
-def download(url, localFileName = None):
-    """Download a single file.
-
-    Extracted from: http://stackoverflow.com/questions/862173/how-to-download-a-file-using-python-in-a-smarter-way
-
-    Keyword arguments:
-    url           -- URL of the file
-    localFileName -- A custom local name for the file. Defaults to None, in this case it uses the original filename
-
-    """
-    localName = url2name(url)
-    req = urllib2.Request(url)
-    r = urllib2.urlopen(req)
-    if r.info().has_key('Content-Disposition'):
-        # If the response has Content-Disposition, we take file name from it
-        localName = r.info()['Content-Disposition'].split('filename=')[1]
-        if localName[0] == '"' or localName[0] == "'":
-            localName = localName[1:-1]
-    elif r.url != url:
-        # if we were redirected, the real file name we take from the final URL
-        localName = url2name(r.url)
-    if localFileName:
-        # we can force to save the file as specified name
-        localName = localFileName
-    log('Baixando novo cardário: %s...' % localFileName)
-    f = open(localName, 'wb')
-    f.write(r.read())
-    f.close()
+    return text
 
 def save(context_file):
     with open(context_file, 'w') as f:
             global context
             context = json.load(f)
 
-def fetch(path, unit, keep):
+def fetch(unit):
     log('Verificando se há uma atualização do cardápio...')
     unit_data = context.get(unit) or {}
     pdf_url = get_pdf_address(sesc_unit[unit]['url'], sesc_unit[unit]['pdf'])
     if pdf_url == unit_data.get('pdf'):
         log('Você já possui o cardápio mais recente.')
         return
-    filename = join(path,url2name(pdf_url))
-    if not os.path.exists(filename):
-        download(pdf_url, filename)
-    content = convert_pdf(filename)
+    else:
+        log('Baixando novo cardápio...')
+    content = get_pdf_as_text(pdf_url)
+    log('Novo cardápio baixado com sucesso.')
     data, date_from, date_to = extract_data(content, sesc_unit[unit])
     unit_data['data'] = data
     unit_data['pdf'] = pdf_url
     unit_data['from'] = date_from
     unit_data['to'] = date_to
     context[unit] = unit_data
-    if not keep:
-        log('Removendo %s...' % filename)
-        os.remove(filename)
     return
 
-def convert_pdf(pdf_file):
-    """ Convert a PDF into a string (It detectcs vertical text)."""
-    log('Analisando cardápio...')
-    rsrcmgr = PDFResourceManager()
-    retstr = StringIO()
-    codec = 'utf-8'
-    laparams = LAParams()
-    laparams.detect_vertical = True
-    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
-
-    fp = file(pdf_file, 'rb')
-    pagenos = set()
-    process_pdf(rsrcmgr, device, fp)
-    fp.close()
-    device.close()
-
-    str = retstr.getvalue()
-    retstr.close()
-    return str
-
 def extract_data(text, unit):
     log('Extraindo dados do cardápio...')
-    years = re.search(unit['years'],text, re.IGNORECASE).groups()
-    months = [m.lower() for m in re.search(unit['months'], text, re.IGNORECASE).groups() if m is not None]
-    if len(months) == 0 or len(years) == 0:
-        raise 'Error parsing months or years.'
    
     # Initial setup
     data = {}
-    last_day = 32
-    last_month = None
-    last_year = None
-    year_index = -1
-    month_index = -1
     date_from = None
     date_to = None
     
         menus =  [menu.replace('\n','') for menu in re.search(unit['menus'], entry.strip(), re.IGNORECASE | re.DOTALL).groups()]
         value = '\n'.join(menus)
 
-        if int(day) < 16 and len(months) > 1:
-            month_index = 1
-        else:
-            month_index = 0
-        month = months[month_index]
-        
-        if get_month_nr(month) < get_month_nr(last_month):
-            year_index += 1
-        year = years[year_index]
-
-        last_day = day
-        last_month = month
-        last_year = year
-
-        timestamp = '{0} {1} {2}'.format(day,month, year)
-        key = get_key(timestamp)
+        key = day
         data[key] = value
 
-        if (date_from is None or time.strptime(key, key_format) < time.strptime(date_from, key_format)):
+        if date_from is None or key < date_from:
             date_from = key
-        if (date_to is None or time.strptime(key, key_format) > time.strptime(date_to, key_format)):
+        if date_to is None or key > date_to:
             date_to = key
     return data, date_from, date_to
 
                 self.mainloop()
 
         def update_menu(self, *args):
-            self.menu.set(context[self.unit.get()]['data'].get(get_key(self.key.get())))
+            self.menu.set(context[self.unit.get()]['data'].get(self.key.get()))
 
         def update_unit(self, *args):
-            self.date['values'] = [key_to_date(key) for key in sorted(context[self.unit.get()]['data'].iterkeys())]
+            self.date['values'] = [key in sorted(context[self.unit.get()]['data'].iterkeys())]
             self.update_menu()
     
     MenuDisplay(title, unit, date)
         write('Opções:')
         write('-a, --all              Exibe o cardápio para todos os dias.')
         write('-c, --clean            Limpa o cache antes de buscar o cardápio.')
-        write('-d, --date=data        Exibe cardápio para uma data específica (dia, mês e ano, separados por qualquer caractere). Caso o ano e o mês sejam omitidos, será considerado o mês atual. Exemplos de datas válidas: "30", "30 Junho", "30-Jun", "30/06", "30+06+13" ou "30_06_2013" ')
-        write('-f, --format=formato   Altera a formatação da data de acordo com os parâmetros aceitos pela função time.strftime do python. O padrão é "%d/%m/%Y".')
+        write('-d, --day=day          Exibe cardápio para um dia específico ')
         write('-g, --gui              Exibe o cardápio em uma janela. Não é compatível com a opção "-a".')
         write('    --guser=username   Configura o nome de usuário do gmail para o envio de e-mail.')
         write('    --gpass=senha      Configura a senha de aplicativo do gmail para o envio de e-mail.')
         write('-h, --help             Exibe esta ajuda.')
         write('-n, --notify=lista     Lista de e-mails, separados por ponto e vírgula, a serem notificados do menu.')
-        write('-k, --keep             Mantém o arquivo PDF do cardápio, padrão é "%s".' % default_keep)
         write('-p, --path=caminho     Caminho onde armazenar os arquivos, padrão "%s".' % default_home)
         write('-q, --quiet            Modo silencioso. Não exibe mensagens intermediárias.')
         write('-u, --unit=unidade     Unidade do SESC, padrão unidade de "%s". Unidades cadastradas: %s' % (default_unit , ', '.join(sesc_unit.keys())))
         write('-v, --version          Exibe a versão do programa.')
         return 100
     try:
-        (opts, args) = getopt.getopt(argv[1:], 'acd:f:ghn:kp:qu:v',['all', 'clean', 'date=', 'format=', 'gui', 'guser=', 'gpass=', 'help', 'notify=', 'keep', 'path=', 'quiet', 'unit=', 'version'])
+        (opts, args) = getopt.getopt(argv[1:], 'acd:ghn:p:qu:v',['all', 'clean', 'day=', 'format=', 'gui', 'guser=', 'gpass=', 'help', 'notify=', 'path=', 'quiet', 'unit=', 'version'])
     except getopt.GetoptError:
         return usage()
 
     show_all = False
     unit = default_unit
     home = default_home
-    keep = default_keep
     clean = False
     quiet = False
-    key = get_key()
+    key = get_current_day()
     gui = False
     notify = None
     guser = None
     for (k, v) in opts:
         if k == '-a' or k == '--all': show_all = True
         elif k == '-c' or k == '--clean': clean = True
-        elif k == '-d' or k == '--date': key = get_key(v)
-        elif k == '-f' or k == '--format': date_format = v
+        elif k == '-d' or k == '--date': key = v
         elif k == '-g' or k == '--gui': gui = True
         elif k == '--guser': guser = v
         elif k == '--gpass': gpass = v
         elif k == '-h' or k == '--help': return usage()
         elif k == '-n' or k == '--notify': notify = v.strip(';').split(';')
-        elif k == '-k' or k == '--keep': keep = not default_keep
         elif k == '-p' or k == '--path': home = v
         elif k == '-q' or k == '--quiet': quiet = True
         elif k == '-u' or k == '--unit': unit = v
             os.remove(context_file)
         context = {'version' : cache_version}
 
-    if unit in context and time.strptime(key, key_format) < time.strptime(context[unit]['from'], key_format):
+    if unit in context and key < context[unit]['from']:
         write('')
         write(key)
         write('Data antiga demais. Não há cardápio disponível.')
         write('')
         sys.exit(1)
 
-    if unit not in context or time.strptime(key, key_format) > time.strptime( context[unit]['to'], key_format):
+    if unit not in context or key > context[unit]['to']:
         log('O menu para o dia não foi encontrado no cache local.')
-        fetch(home, unit, keep)
+        fetch(unit)
         save(context_file)
 
     if show_all:
         write('')
         for key in sorted(context[unit]['data'].iterkeys()):
-            write(key_to_date(key))
+            write(key)
             write(context[unit]['data'][key])
             write('')
     else:
         if notify and guser and gpass:
             data = context[unit]['data'].get(key) or 'Não há menu disponível para esse dia.'
             write('Notificando as seguintes pessoas: %s' % notify)
-            notify_all(guser, gpass, unit,  key_to_date(key), data, notify)
+            notify_all(guser, gpass, unit,  key, data, notify)
 
         if gui:
-            show_dialog(os.path.basename(argv[0]), unit, key_to_date(key))
+            show_dialog(os.path.basename(argv[0]), unit, key)
         else:
             write('')
-            write(key_to_date(key))
+            write(key)
             data = context[unit]['data'].get(key) or 'Não há menu disponível para esse dia.'
             write(data)
             write('')