domingo, 17 de marzo de 2019

Y más sobre Python 2.7:

¡¡ OJO al comparar con booleanos !!:
  • 0 == False => True
  • 1 == True => True
Parece que hace una conversión implícita de False/True a int...
La forma segura es:
  • 0 is False => False
  • 1 is True => False
Por tanto, la conclusión tanto con booleanos como con None, es comparar siempre usando 'is' en vez de '=='.



La representación de Float es un dolor...

  • ut = 1547833469.123
  • print(ut) -> 1547833469.12
  • str(ut) -> 1547833469.12
  • repr(ut) -> 1547833469.123
  • print repr(ut) -> 1547833469.123
  • float(ut) -> 1547833469.123
  • print float(ut) -> 1547833469.12
Esto nos puede volver absolutamente locos usando print para depurar (str implícito), pero también puede dar problemas enormes componiendo instrucciones SQL.

miércoles, 29 de julio de 2015

Útiles GNU/Linux

  • Montar una imagen CD/DVD Nero:
    mount -o loop,offset=307200 imagen.nrg /punto/de/montaje
     
  • Arreglar grub: después de volver a cargar mi imagen de partición de sistema con Clonezilla, puede que el grub deje de funcionar. Para arreglarlo hay 2 formas:
  1. Arrancar con LiveCD, montar la partición donde está el Grub, reasignar la raíz activa a ese directorio y reinstalar grub ahí:
    sudo mkdir /mnt/raiz
    sudo mount /dev/sda4oloquesea /mnt/raiz
    sudo chroot /mnt/raiz
    sudo grub-install /dev/sda4
  2. Arrancar con SuperGrubDisk => se detecta el grub dañado y permite que arranquemos nuestro sistema normalmente. Después arreglamos el Grub de forma permanente:
    sudo grub-install /dev/sda4
  • Para evitar que la consola SSH remota se congele, editar /etc/ssh/ssh_config y añadir ServerAliveInterval 30
  • Para cambiar el nombre de la máquina por XXXX: sudo hostnamectl set-hostname XXXX (y después sudo nano /etc/hosts)
  • ejecutar el último comando como root: sudo !!

  • Alias interactivo: alias cp='cp -i' alias mv='mv -i' alias rm='rm -i'
  • Alias cómodo: alias mkgz="tar -cvzf" alias untar="tar -xvzf"
  • Para que funcionen los alias con sudo hay que convertirlo a él mismo en un alias: alias sudo='sudo '
  • Función alias: crear ~/.bash_aliases con: ghist() { history | grep -F $1 | grep -Fv ghist | tail -n 12; }
  • Sincronizar directorios local y remoto: rsync -avz /var/dir/ user@server:/var/ 
  • Si nos olvidamos de usar nohup, podemos "desligar" un proceso background a posteriori con disown jobID

  • Crear directorios anidados: mkdir -p tmp/foo/bar 
  • Alternar entre 2 directorios: cd -

  • Escribir comando de varias líneas: fc
  • Averiguar IP externa: curl ipconfig.me  curl ipconfig.me/ip
  • Tabla Ascii: man ascii
  • Colores en Xterm Bash: editamos .bashrc descomentando 'force_color_prompt=yes' y modificamos PS1 de 'if [ "$color_prompt" = yes ];' para que sea cómodo pero no molesto: PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u\[\033[00m\]@\[\033[01;34m\]\h\[\033[00m\]:\w\$ '. Para PC local se puede usar otro color para el servidor (p.e. \[\033[01;33m\]\h) y para usuario root recomiendo modificar su .bashrc con otro color en el usuario, para que salte a la vista que estamos como root: PS1='${debian_chroot:+($debian_chroot)}\[\033[01;31m\]\u...'.
  • Paquetes útiles: synaptic, gdebi-core, htop, nload, git, diffuse, bless, g++, apt-show-versions, nautilus-open-folder, openvpn, network-manager-openvpn, python-tk, python-dev, python-setuptools
  •  SSH Port-Forwarding para mantenimiento en vez de configurar OpenVPN:
    ssh -L 3301:127.0.0.1:3306 ubuntu@myserver... permite acceder a Mysql como si estuviéramos en local mientras no salgamos de esa sesión (no basta solo con --port=3301, también es necesario --host=127.0.0.1)

jueves, 18 de junio de 2015

MultiMySQL

Guide to run multiplie MySQL instances on same Ubuntu 14.04 Server.
Based mainly on http://java.dzone.com/articles/mysqldmulti-how-run-multiple.

We have an instance, that will "rename" to 'mysql0', and we'll add another one: 'mysql3307'.

/etc/init.d/mysql stop

sudo mv /etc/init.d/mysql /etc/init.d/mysql_mono.server (backup)
sudo cp /usr/share/mysql/mysqld_multi.server /etc/init.d/mysql

edit /etc/init.d/mysql, then find the lines:
  • basedir=/usr/local/mysql
  • bindir=/usr/local/mysql/bin
and change to:
  • basedir=/usr
  • bindir=/usr/bin
sudo cp -p /etc/mysql/my.cnf /etc/my.cnf.mono (backup)
edit /etc/mysql/my.cnf to rename [mysqld] to [mysqld0] and to add [mysqld3307] (changes pid-file, socket, port and datadir; also logdir, serverid...)

Also add this to /etc/mysql/my.cnf:
    [mysqld_multi]
    mysqld     = /usr/bin/mysqld_safe
    mysqladmin = /usr/bin/mysqladmin
    user       = multi_admin
    password   = multipass

put additional rules to deal with 3307 file/dirs in the application firewall /etc/apparmor.d/local/usr.sbin.mysqld (https://github.com/naveensnayak/mysql-multi/blob/master/usr.sbin.mysqld) or /etc/apparmor.d/usr.sbin.mysqld?
sudo service apparmor reload

sudo mysql_install_db -verbose --user=mysql --datadir=/var/lib/mysql3307

sudo mkdir /var/log/mysql3307
sudo chown mysql:mysql /var/log/mysql3307
Better rename log files in /var/log/mysql instead of create a new dir with same file names?

sudo /etc/init.d/mysql start 0 (mysqladmin -h 127.0.0.1 --port=3306 -u root start)
mysql -h 127.0.0.1 --port=3306 -u root -p
  • mysql> GRANT SHUTDOWN ON *.* TO 'multi_admin'@'localhost' IDENTIFIED BY 'multipass';
  • mysql> FLUSH PRIVILEGES;

repeat GRANT for mysql3307 connection [mysql -h 127.0.0.1 --port=3307 -u root -p (empty password)]
and give root a password:
    mysql> update mysql.user set password=PASSWORD('myRootPassword') where User='root'; flush privileges;

Manage instances:
  • sudo mysqld_multi report
  • sudo mysqld_multi start 3307
  • sudo mysqld_multi stop 0,3307

This is to make client access easier:
    create /usr/sbin/mysql3307, and put the following lines on it:
        #!/bin/bash
        mysql --socket=/var/run/mysqld3307.sock $1 $2 $3 $4 $5 $6 $7 $8
    (maybe better with: mysql -h 127.0.0.1 --port=3307 $1 $2 $3 $4 $5 $6 $7 $8)
    sudo chmod a+x /usr/sbin/mysql3307

    # mysql3307
    mysql> select @@server_id;

Finally make sure instances are loaded automatically at startup.

--------------

If you want the new instance to be a slave of a master in another server, you can follow this:
http://java.dzone.com/articles/mysqldmulti-how-run-multiple (this related: http://dba.stackexchange.com/questions/41050/is-it-safe-to-delete-mysql-bin-files)

lunes, 19 de mayo de 2014

Más sobre Python

Aparte de completar la entrada anterior sobre Python, incluyo aquí aquellas cosas que me han vuelto loco en su momento. En especial, las pequeñas GRANDES cosas que no se mencionan en ningún tutorial sobre Python y que he descubierto con sangre.

Conclusiones:
  1. Sigue siendo mi lenguaje preferido.
  2. El manejo de husos horarios en Python es la peor experiencia de programación de mi vida.
  3. Los tutoriales de iniciación obvian unas pocas cosas muy importantes al empezar a programar en serio.

if var

Es el "idiom" más recurrente de Python, y normalmente se explica como "si var tiene algo".

Si se aplica siempre está lógica, morirás en el intento, porque realmente significa:
"si var no es None y no es el valor por defecto para este tipo de variable".
Por tanto estas cosas son True:
  • bool('0') 
  • bool(' ') 
  • bool(-1)
  • bool([0])
Y estas cosas son False:
  • bool(0)
  • bool('')
  • bool(None)
  • bool([])
Ya que el valor por defecto para una cadena de texto es la cadena vacía, para un entero es el cero, para una lista es la lista vacía, etc.

Copia de colecciones

Cuando copiamos una lista o diccionario, estamos copiando en realidad la referencia al objeto, con lo cual si modificamos, el original, se modifica también la copia.
Para evitar esto hay que usar el método 'deepcopy' de la librería 'copy':
lista2 = copy.deepcopy(lista1)
En los casos en que nos hay colecciones anidadas, puede bastar una copia superficial (shallow), que puede hacer de estas formas con los diccionarios:
dicc2 = dict(dicc1)
dicc2 = dicc1.copy()
dicc2 = copy.copy(dicc1)
y de estas formas con las listas:
lista2 = list(lista1)
lista2 = copy.copy(lista1)

Paso de variables a funciones

En realidad sigo sin tener claro como funciona, pero sí puedo advertir de serios efectos colaterales si no se tienen en cuenta ciertas cosas.

En Python se utiliza el paso por valor de referencias a objetos , aunque los valores inmutables (enteros, tuplas...) se comportan como paso por valor. Esto provoca que al pasar un objeto mutable no siempre estemos modificando el objeto original. P.e. Estas dos expresiones son idénticas donde se define el diccionario, pero son completamente distintas si se utilizan sobre una referencia recibida:
dicc = {} # ésta no borra el diccionario "original"
dicc.clear() # ésta sí
En cuanto a las variables globales, se puede acceder directamente a ellas, para lectura, desde dentro de una función, siempre que no exista una variable local con su mismo nombre. Pero si intentamos modificar su valor, en realidad creará al vuelo una variable local con su mismo nombre. Para poder modificar una variable global debemos declarla así, dentro de la función:
global mi_variable_global
Nota: si la variable global es mutable (p.e. un diccionario) y no la declaramos con 'global' dentro de una función, no podremos hacer que apunte a otro objeto, pero sí podremos alteraTruer su contenido. Por tanto, nuevamente, dicc={} no borraría el diccionario original, pero dicc.clear(), sí, y dicc['campo1']=3 alteraría el contenido de la variable global.

Vaciar lista

Esto crea una lista vacía, pero no vacía una existente:
l = []
Para vaciar una existente no existe un método como el 'clear' de los diccionarios, sino que hay que hacer:
l[:] = [] # equivalente: del l[:]
No es muy fácil de recordar, pero como se intuye, mediante slicing podemos vaciar solo una parte de la lista:
l[2:3] = [] # equivalente: del l[2:3]

Operadores * y **

Ambos tienen sentido solo al pasar parámetros a una función. Hablaré de sucesión o secuencia de parámetros, ya que la palabra "lista" en Python es un tipo de datos. Para mí, la secuencia de parámetros es una cosa rara, que bien se podría asimilar a unos de los tipos o darle una entidad más explícita.

La secuencia de parámetros en la llamada a una función solo puede contener valores sin nombre y valores con nombre separados por comas. Estos últimos tienen que ir al final obligatoriamente, aunque el orden entre ellos no tiene por qué ser el mismo que en la definición de la función.

Al hilo de esto:
  • * convierte una lista en una sucesión de parámetros sin nombre
  • ** convierte un diccionario en una sucesión de parámetros con nombre
Estos operadores sirven para pasar a una función un número variable de parámetros. Cuidado con usar * con un diccionario, ya que el compilador no se quejará, pero por lo general no tiene sentido y su efecto puede ser aleatorio (al no ser los diccionarios ordenados).

Ordenar un diccionario

En Python 2.7 se introdujeron los SortedDict. Para Python 2.6 se puede usar una implementación completa, añadiendo una librería, o bien, en algunos casos, basta con usar este truco simple para recorrerlo ordenado (siendo d un diccionario):
for k in sorted(d):
    print d[k]

También:
  • sorted(d.iterkeys()) 
  • sorted(d.keys())

Doctest

Considero Doctest muy útil por ser una forma tremendamente compacta de implementar pruebas unitarias, pero tiene algún comportamiento extraño derivado del hecho de que la parte de comprobación, es una cadena de texto:
  • El valor que devuelve no siempre tiene la forma esperada, en el caso de un string, por lo que la cadena de comprobación puede tener que ser diferente a la que se visualiza.
  • No se puede poner ningún tipo de expresión en la cadena de comprobación, por lo que en estos casos, tenemos que evaluar una comparación contra dicha expresión y comprobar si devuelve True o False

jueves, 12 de septiembre de 2013

Nociones de Python


Megachuletario para uso personal
(basado principalmente en el PDF "Python para todos")
Tipos
Python es de tipado dinámico (no se declaran las variables, el tipo lo toman de aquello que se les asigna y pueden cambiar de tipo) pero fuertemente tipado (las conversiones de tipo deben ser explícitas).



Hola mundo (python "hola_mundo.py"):
#!/usr/bin/env python
# coding: utf-8

mi_variable = "Hola mundo"
print type(mi_variable)



El tipo int de Python se implementa a bajo nivel mediante un tipo long de C. En la mayor parte de los casos, 32 bits, es decir, de -2^31 a 2^31 – 1 (-2.147.483.648 a 2.147.483.647). En plataformas de 64 bits, el rango es de -9.223.372.036.854.775.808 hasta 9.223.372.036.854.775.807.
El tipo long de Python permite almacenar números de cualquier precisión, estando limitados solo por la memoria disponible en la máquina.
Al asignar un número a una variable esta pasará a tener tipo int, a menos que el número sea tan grande como para requerir el uso del tipo long.
Long explícito: 23L
Octal explícito: 027
Hexadecimal explícito: 0x17
Números reales: 0.0001 y 0.1e-3
Operadores de conversión explícitos: float(), long(), bool(), int(), complex().
None: equivalente a Null de otros lenguajes.
Operadores
Asignación:
a = b = 2
print a
print b
print a == b


Exponente: 2 ** 8
División entera: 7 // 2
Módulo: 7 % 2
Aunque usemos el operador de división no entera (/), si ambos operandos son enteros, el resultado también lo será. Por tanto para asegurar que el resultado tenga decimales debemos asegurar que al menos uno de los operandos sea real. P.e.: 3.0 / 2 o bien float(3)/2.
Operadores binarios: & | ^ ~ << >>
'una' "dos" 'tres\ncuatro' # Cadenas de texto
u'eñe' # Unicode
r'\n' # Raw (no se hace sustitución de caracteres)
'''Primera
   Segunda''' # Cadena en varias líneas
'hola' + ' ' + 'mundo' # Concatenación de cadenas
'.'*10 # Repetición de cadenas
c = 'hola mundo'
c[0] # h 
c[-2] # d
c[5:7] # mu 
c[2::3] # lmd
c[:3] # 3 primeros caracteres
c[-3:] # 3 últimos caracteres



Booleanos y relacionales:
 True, False, and, or, not, ==, !=, <, >, <=, >=.
Colecciones
Listas
l = [22, True, 'hola', [11, 33]]
l[3][0] # 11
len(l) # 4
l[-1] = 'adiós' # sustituye [11, 33] por 'adiós'
l[0:2] = ['Uno por dos'] # l vale ['Uno por dos', 'hola', 'adiós']
l = [] # lista vacía
Tuplas (listas inmutables y más ligeras)
l = (1,) # la coma final diferencia la tupla de un entero entre paréntesis
type(l) #  
l = (1) # no es una tupla
type(l) # 
l = () # tupla vacía
Conjuntos
Un conjunto es una colección no ordenada y sin elementos repetidos :
>>> canasta = ['manzana', 'naranja', 'manzana', 'pera', 'naranja', 'banana'] 
>>> fruta = set(canasta) # crea un conjunto sin repetidos 
>>> fruta 
set(['pera', 'manzana', 'banana', 'naranja']) 
>>> 'naranja' in fruta # verificación de pertenencia rápida 
True 

>>> a = set('abracadabra') 
>>> b = set('alacazam') 
>>> a # letras únicas en a 
set(['a', 'r', 'b', 'c', 'd']) 
>>> a – b # letras en a pero no en b 
set(['r', 'b', 'd']) 
>>> a | b # letras en a o en b 
set(['a', 'c', 'b', 'd', 'm', 'l', 'r', 'z']) 
>>> a & b # letras en a y en b 
set(['a', 'c']) 
>>> a ^ b # letras en a o b pero no en ambos 
set(['b', 'd', 'm', 'l', 'r', 'z']) 
Diccionarios
Las claves tienen que ser inmutables (números, cadenas, booleanos, tuplas... ).
d = {'Clave 1': 'Valor 1',
  'Clave 2': 'Valor 2',
  'Clave 3': 'Valor 3'}
d['Clave 2'] = 'Nuevo Valor 2'
d['Clave 4'] = 'Valor de la nueva clave'
'Clave 3' in d # True
len(d) # 3
Control de flujo
if numero < 0: 
 print “Negativo” 
elif numero > 0: 
 print “Positivo” 
 print '...aunque si numero es una cadena también entra aquí...'
else: 
 print “Cero” 


var = “par” if (num % 2 == 0) else “impar” # A if C else B 

Si la rama de ejecución tiene una solo línea se puede escribir de forma compacta:
if numero < 0: print “Negativo” 
else: print “Positivo o Cero” 


No existe estructura como 'switch' o 'select...case'.
Bucles
i = 0
while i < 5:
 entrada = raw_input("> ")
 if entrada == "fin":
  break # sale del bucle 'while' o 'for' más interior
 elif entrada == "paso":
  continue # fuerza la siguiente iteración sin esperar a que finalice ésta
 else:
  print entrada
 i += 1
else:
 print 'salimos del bucle porque van 5 entradas; si saliésemos con "break" no se ejecutaría esto'


lista = ['uno', 'dos', 'tres']
for elemento in lista:
 print elemento


for i in range(0, 5, 2):
 print i # 0 2 4
 if (i % 2) > 0: break # acabamos en cuanto encontremos el primer impar
else:
 print 'todos los números eran pares'
Funciones
Número variable de argumentos mediante una tupla:
def mi_funcion2(param1, param2 = 'por defecto', *otros):
 print param1
 print param2
 for val in otros:
  print val

def mi_funcion3():
 pass # definimos la función vacía (p.e. Para rellenarla más tarde)

mi_funcion2(1)
mi_funcion2(1, 2, 3, 'cuatro', 5)



Número variable de argumentos mediante un diccionario:
def varios(param1, param2, **otros): 
 for i in otros.items(): 
  print i 

varios(1, 2, tercero = 3, cuarto = 'cuatro') 



En Python se utiliza el paso por valor de referencias a objetos , aunque los valores inmutables (enteros, tuplas...) se comportan como paso por valor.



El ejemplo anterior es como un procedimiento, y éste es como una función que devuelve una tupla:
def f(x, y): 
 return x * 2, y * 2 

a, b = f(2, 3) 
Orientación a Objetos
Clases y objetos
class Coche: 
 “””Abstraccion de los objetos coche.””” 
 def __init__(self, gasolina): 
  self.gasolina = gasolina 
  print “Tenemos”, self.gasolina, “litros” 

 def conducir(self): 
  if self.gasolina > 0: 
   self.gasolina -= 1 
   print “Quedan”, self.gasolina, “litros” 
  else: 
   print “No se mueve” 


mi_coche = Coche(3)
mi_coche.conducir()



Clase como struct de C:
class Estructura:
 pass # la definimos vacía

mio = Estructura()
mio.atributo1 = 'S.O. GNU'
mio.atributo2 = 'FLOSS'
Herencia
class Terrestre:
 def desplazar(self):
  print 'El animal anda'
  
 def respirar(self):
  print 'Respira fuera del agua'

class Acuatico:
 def desplazar(self):
  print 'El animal nada'

 def respirar(self):
  print 'Respira bajo el agua'

class Cocodrilo(Terrestre, Acuatico): #heredamos de ambas pero prevalece 'Terrestre'
 def desplazar(self): #sobreescrimos los métodos heredados
  print "Repta"
 

c = Cocodrilo()
c.desplazar()
Acuatico.desplazar(c) #usamos el método de una de la superclases de las que hereda
c.respirar()
Encapsulación
class Ejemplo:
 def publico(self):
  print 'Publico'

 def __privado(self):
  print 'Privado'

ej = Ejemplo()

ej.publico()
ej.__privado() # da error
ej._Ejemplo__privado() # truco para acceder al método "oculto"


class Fecha():
 def __init__(self):
  self.__dia = 1

 def getDia(self):
  return self.__dia

 def setDia(self, dia):
  if dia >= 1 and dia <= 31:
   self.__dia = dia
  else:
   print 'No es un día'

mi_fecha = Fecha()
print mi_fecha.getDia()
mi_fecha.setDia(3)
mi_fecha.setDia(33)
print mi_fecha.getDia()


class Fecha2(object): # object es un objeto vacío de nuevo-estilo necesario para 'property'
 def __init__(self):
  self.__dia = 1

 def getDia(self):
  return self.__dia

 def setDia(self, dia):
  if dia >= 1 and dia <= 31:
   self.__dia = dia
  else:
   print 'No es un día'
 
 dia = property(getDia, setDia)

mi_fecha2 = Fecha2()
print mi_fecha2.dia
mi_fecha2.dia = 3
mi_fecha2.dia = 33
print mi_fecha2.dia
Métodos especiales
__init__(self, args) 
__new__(cls, args) 
__del__(self) 
__str__(self) 
__cmp__(self, otro) 
__len__(self) 
Métodos de objetos
Diccionario.get(k[, d]) 
Diccionario.has_key(k) 
Diccionario.items() 
Diccionario.keys() 
Diccionario.pop(k[, d]) 
Diccionario.values() 
Cadena.count(sub[, start[, end]]) 
Cadena.find(sub[, start[, end]]) 
Cadena.join(sequence) 
Cadena.partition(sep) 
Cadena.replace(old, new[, count]) 
Cadena.split([sep [,maxsplit]]) 
Cadena.lower()
Cadena.upper()
Cadena.swapcase()
Cadena.title()
Cadena.strip()
Lista.append(object) 
Lista.count(value) 
Lista.extend(iterable) 
Lista.index(value[, start[, stop]]) 
Lista.insert(index, object) 
Lista.pop([index]) 
Lista.remove(value) 
Lista.reverse() 
Lista.sort(cmp=None, key=None, reverse=False) 
Funciones de orden superior
def funcion():
 print 'Hola'

f = funcion
f()
Iteraciones de orden superior sobre listas
def cuadrado(n):
 return n ** 2
l = [1, 2, 3]
print map(cuadrado, l)


def es_par(n):
 return (n % 2.0 == 0)
l = [1, 2, 3]
print filter(es_par, l)


l = [1, 2, 3] 
print filter(lambda n: n % 2.0 == 0, l) 


def sumar(x, y):
 return x + y
l = [1, 2, 3]
print reduce(sumar, l)



Comprensión de listas
Se aconsejan en lugar de 'map' y 'filter'.
l = [0, 1, 2, 3] 

l2 = [n ** 2 for n in l] 

l2 = [n for n in l if n % 2.0 == 0] 

m = ['a', 'b'] 
n = [s * v for s in m 
  for v in l 
  if v > 0] 
Generadores
Función que genera valores sobre los que iterar . Son como listas pero en lugar de estar completas en memoria, se generan poco a poco.
Decoradores
def mi_decorador(funcion):
 def nueva(*args):
  print 'Llamada a la funcion', funcion.__name__
  retorno = funcion(*args)
  return retorno
 return nueva



Así todas las llamadas a 'imprime' son "decoradas":
@mi_decorador
def imprime(texto):
 print texto
 
imprime('Hola')



Así solo se decoran las llamadas deseadas:
def imprime(texto):
 print texto
 
mi_decorador(imprime)('Hola')
Excepciones
a = 1
b = 0
try:
 r = a / b
except (NameError, ValueError):
 print 'La variable no existe o el valor no es un número'
except:
 print 'otro tipo de error'
else:
 print 'sin errores'
finally:
 print 'Se ejecuta siempre al final de todo'



Otros resultados cambiando 'b=0' por 'c=0' y por 'b=1'.
También podemos definir y lanzar nuestras propias excepciones:
class MiError(Exception):
 def __init__(self, valor):
  self.valor = valor

 def __str__(self):
  return 'Error ' + str(self.valor)

resultado = 22
try:
 if resultado > 20:
  raise MiError(33)
except MiError, e:
 print e



Lista de excepciones por defecto:
BaseException, Exception, GeneratorExit, StandardError, ArithmeticError, FloatingPointError, OverflowError, ZeroDivisionError, AssertionError, AttributeError, EOFError, EnvironmentError, IOError, OSError, WindowsError, ImportError, LookupError, IndexError, KeyError, MemoryError, NameError, UnboundLocalError, ReferenceError, RuntimeError, NotImplementedError, SyntaxError, IndentationError, TabError, SystemError, TypeError, ValueError, UnicodeError, UnicodeDecodeError, UnicodeEncodeError, UnicodeTranslateError, StopIteration, Warning, DeprecationWarning, FutureWarning, ImportWarning, PendingDeprecationWarning, RuntimeWarning, SyntaxWarning, UnicodeWarning, UserWarning, KeyboardInterrupt, SystemExit.
Módulos y Paquetes
Módulos
import time
print time.asctime()


from time import asctime
print asctime()


if __name__ == “__main__”: 
 print “Se muestra si no es importacion sino que lo ejecutamos directamente” 



Paquetes
Un paquete es un directorio que contiene varios módulos, que son archivos. Asimismo debe contener un archivo llamado __init__.py:
import paq.subpaq.modulo 
paq.subpaq.modulo.func()
Entrada/Salida Y Ficheros
Entrada y salida
try: 
 edad = raw_input(“Cuantos anyos tienes? “) 
 dias = int(edad) * 365 
 #print 'Has vivido ' + dias + ' dias' # incorrecto
 print 'Has vivido ' + str(dias) + ' dias' # correcto
 print 'Has vivido', dias, 'dias' # correcto
 
 print 'Línea 1.',
 print 'Sigo en la línea 1.\tTabulado.'
except ValueError: 
 print “Eso no es un numero”
 
 print 'adecuado para humanos:', str(0.1), ' ,, para intérprete:', repr(0.1)


import sys
for i in range(0, len(sys.argv)):
 print sys.argv[i],


Formateo de cadenas:
from math import pi

# vieja manera
print '%8s %-18.16s %x %.4f' % ('Hola', 'Hexadecimal y Piiiii', 255, pi)

# nuevas maneras
print '{0} {1:18.16s} {2:x} {3:.4f}'.format('Hola'.rjust(8), 'Hexadecimal y Piiiii', 255, pi)
print '{0} {1:18.16s} {3:x} {2:.4f}'.format('Hola'.rjust(8), 'Hexadecimal y Piiiii', pi, 255)
print '{0} {1:18.16s} {hexa:x} {2:.4f}'.format('Hola'.rjust(8), 'Hexadecimal y Piiiii', pi, hexa=255)
Archivos
‘r’: read. El archivo tiene que existir previamente, sino excepción de tipo IOError.
‘w’: write. Si el archivo no existe se crea. Si existe, sobreescribe el contenido.
‘a’: append. Se comienza a escribir al final del archivo.
‘b’: binary, binario.
‘+’: permite lectura y escritura simultáneas.
‘U’: universal newline .



Métodos: read, readline, readlines, write, writelines, seek, tell.
f = open('archivo.txt', 'w')
f.writelines('línea 1\n')
f.close()
f = open('archivo.txt', 'a')
f.write('lín')
f.writelines('ea 2\n')
f.close()

f = open('archivo.txt', 'r')
print f.read(5) # bytes (no caracteres)
print f.tell()
f.seek(-1, 2) # desde el final
print f.tell()
f.seek(4, 0) # desde el principio (por defecto)
print f.tell()
f.seek(-1, 1) # desde la posición actual
print f.tell()
l = f.readlines()
for e in l: print e,
f.close()



Fichero temporal que se elimina automáticamente al cerrarlo:
tempfile.TemporaryFile()
Expresiones Regulares
import re
if re.match('(c|p|j)yth..', 'python'): print 'coincide'
if re.match('\.config', '.config'): print 'coincide'
if re.match('\.config|\.setup', '.config'): print 'coincide'
if re.match('[pjc]yt[a-zA-Z]on[^a-zA-Z][.,]', 'python2.'): print 'coincide'
if re.match('^http', 'http://mundogeek.net'): print 'coincide'


'\d': Equivale a [0-9]
'\D': Equivale a [^0-9]
'\w': Equivale a [a-zA-Z0-9_]
'\W': Equivale a [^a-zA-Z0-9_]
'\s': Equivale a [ \t\n\r\f\v]
'\S': Equivale a [^ \t\n\r\f\v]


Lo que tenemos a la izquierda...
'+': puede aparecer una o mas veces.
'*': puede aparecer cero o mas veces.
'?': puede aparecer cero o una vez.
'{3,8}': tiene que aparecer de 3 a 8 veces.



're.match' tiene un tercer parámetro opcional que puede tomar como valor: 're.IGNORECASE ', 're.VERBOSE '. Devuelve 'None' o un objeto de tipo MatchObject con métodos 'start', 'end', 'group' y 'groups '.
're.search' funciona de forma similar a 're.match ', aunque buscamos cualquier parte de la cadena que se ajuste al patrón , mientras que con 're.match' la cadena debe ajustarse al patrón desde el primer carácter de la cadena .
're.findall' devuelve una lista con las subcadenas que cumplieron el patrón.
're.finditer' devuelve un iterador con el que consultar uno a uno los distintos MatchObject.
're.split' y 're.sub' son como 'split' y 'sub' admitiendo expresiones regulares. Para mejorar su rendimiento podemos crear un objeto RegexObject con 're.compile'.
Sockets
Módulo 'socket '. Sockets de flujo 'socket.SOCK_STREAM' (TCP) y sockets de datagramas 'socket.SOCK_DGRAM' (UDP). Familia 'socket.AF_INET', 'socket.AF_INET6'...
Métodos recv y send (en TCP) y recvfrom y sendfrom (en UDP) .
Servidor
import socket 
socket_s = socket.socket() 
socket_s.bind((“localhost”, 9999)) 
socket_s.listen(10)  # máximo 10 conexiones
socket_c, (host_c, puerto_c) = socket_s.accept() 

recibido = socket_c.recv(1024)  # recogemos 1024 bytes como máximo
print “Recibido: “, recibido 
socket_c.send(recibido) 
socket_c.close() 
socket_s.close() 
Cliente
import socket 
s = socket.socket() 
s.connect((“localhost”, 9999)) 
s.send(mensaje) 
s.close() 



Interactuar con webs
import urllib, urllib2

try:
 f = urllib2.urlopen('http://www.python.org')

 #Modificamos UserAgent
 o_request = urllib2.Request('http://www.python.org', headers={'User-Agent': 'Cliente Http Python'})
 f = urllib2.urlopen(o_request)
 
 params = urllib.urlencode({'usuario': 'manuel', 'password': 'contraseña'})

 #Parámetros por POST
 f = urllib2.urlopen('http://ejemplo.com/login', params)

 #Parámetros por GET
 f = urllib2.urlopen('http://ejemplo.com/login' + '?' + params)

 print f.read()
 f.close()
except:
 print 'Ocurrió un error' # si no queremos hacer nada pondríamos 'pass'



Threads
En Python podemos crear nuevos procesos mediante la función os. fork, o mediante otras funciones más avanzadas como popen2.popen2. Sin embargo el cambio de contexto puede ser relativamente lento, y los recursos necesarios, demasiados .
Los Threads (tb. llamados hilos o procesos ligeros ) son más ligeros que los procesos, el cambio de contexto es más rápido y dado que los threads comparten el mismo espacio de memoria global, cualquier variable global que tengamos en nuestro programa es vista por todos los threads.



import threading, time, random

class MiThread(threading.Thread):
 def __init__(self, num, nombre):
  threading.Thread.__init__(self)
  self.num = num # atributo creado por nosotros
  self.name = nombre # atributo existente en la clase (por defecto 'Thread-1'...)
 
 def run(self):
  print 'Soy el hilo nº', self.num, self.name
  time.sleep(random.random() * 0.3) # esperamos entre >= 0 y < 0.3

print 'Soy el hilo principal'

for i in range(0, 10):
 t = MiThread(i, 'mi_hilo' + str(i))
 t.start()
 t.join() # espera x seg. a que finalice la ejecución del hilo
Sincronización
Para sincronizar el acceso a ciertos recursos por parte de los threads tenemos mecanismos de sincronización : locks (también llamados mutex, cierres de exclusión mutua o candados), locks reentrantes y semáforos.
lista = [] 

lock = threading.Lock() 

def anhadir(obj): 
 lock.acquire()  # se queda bloqueado aquí hasta que el candado esté libre
 lista.append(obj) 
 lock.release() 

def obtener(): 
 # indicamos con False que no queremos esperar
 # si devuelve True es que nos lo han dado (estaba libre)
 if lock.acquire(False) == True:
 obj = lista.pop() 
 lock.release() 
 return obj 



La clase RLock funciona de forma similar a Lock, pero en este caso el candado puede ser adquirido por el mismo thread varias veces, y no quedará liberado hasta que el thread llame a release tantas veces como llamó a acquire.
Los semáforos son otra clase de candados en que el constructor de Semaphore puede tomar como parámetro opcional el número máximo inicial de threads que pueden acceder a la vez a la sección de código crítico. La clase BoundedSemaphore impide que se supere el número máximo inicial.
Otras formas de sincronizar son las Condiciones, los Eventos y el uso de un decorador para hacer que sólo un thread pueda acceder al método sobre el que se utiliza a la vez .
Datos globales independientes
Para ocultar las variables del hilo principal a los hilos hijo utilizamos threading.local() donde creamos atributos para "ocultar" datos.
Compartir información
Para compartir información entre los threads de forma sencilla podemos utilizar la clase Queue.Queue . Nos ahorra tener que sincronizar el acceso a los datos nosotros mismos. Los métodos put(item) y get() tienen un parámetro booleano opcional block que indica si queremos que se espere hasta que haya algún elemento disponible y otro timeout que indica, en segundos, el tiempo máximo a esperar. Con qsize obtenemos el tamaño de la cola y con empty() y full() podemos comprobar si está vacía o llena.
q = Queue.Queue() 

class MiThread(threading.Thread): 
 def __init__(self, q): 
  self.q = q 
  threading.Thread.__init__(self) 


 def run(self): 
  while True: 
   try: 
    obj = q.get(False) 
   except Queue.Empty: 
    print “Fin” 
    break 
   print obj 
for i in range(10): 
 q.put(i) 

t = MiThread(q) 
t.start() 
t.join() 


Serialización de objetos
try:
 import cPickle as pickle
except ImportError:
 import pickle

fichero = file('datos.dat', 'w')
animales = ['pitón', 'mono', 'camello']

pickle.dump(animales, fichero, 1) # 0=texto (por defecto) ,, 1=binario

fichero.close()

fichero = file('datos.dat')

animales2 = pickle.load(fichero)
print animales2

fichero.close()



El módulo shelve extiende pickle / cPickle para acceder a la versión serializada de un objeto mediante una cadena asociada, a través de una estructura parecida a un diccionario. Como un diccionario cualquiera la clase Shelf cuenta con métodos get, has_key, items, keys, values...
Bases de Datos
Python incorpora un módulo compatible con esta base de datos que sigue la especificación de DB API 2.0: sqlite3.
La sintaxis a utilizar para insertar valores en la consulta SQL de forma dinámica es qmark:
sql = “select all from t where valor=?” 



Ejemplo:
import sqlite3 as dbapi

conn = dbapi.connect("bbdd.dat") # opcional :memory:
cursor = conn.cursor()

cursor.execute("drop table empleados")
cursor.execute("""create table empleados (dni text, nombre text, departamento text)""")

cursor.execute("""insert into empleados
      values ('12345678-A', 'Manuel Gil', 'Contabilidad')""")

conn.commit() #no es necesario si tenemos autocommit pero no está de más para dejar el código preparado para otro caso/BDD

cursor.execute("""select * from empleados where departamento='Contabilidad'""")

for tupla in cursor: # tb. cursor.fetchone(), cursor.fetchmany(n) o cursor.fetchall() ; (si no se indica n, se toma cursor.arraysize que vale 1 por defecto)
 print tupla

for t in [('87654321-Z', 'Pepe Mel', 'Informatica'),
          ('dni2', 'Yo', 'sistemas')]:
    cursor.execute('insert into empleados values (?,?,?)', t)

# ¿esto es equivalente?
c.executemany('insert into empleados values (?,?,?)',
 [
 ('87654321-Z', 'Pepe Mel', 'Informatica'),
 ('dni2', 'Yo', 'sistemas')
 ] )


conn.rollback()

t = ('Informatica',)
cursor.execute("""select * from empleados
                  where departamento=?""", t) # evitamos inyección de código

print cursor.fetchone() # Dará None por el rollback

cursor.close()
conn.close()



La API de bases de datos de Python incluye una serie de constructores :
  • Date(year, month, day): Para almacenar fechas.
  • Time(hour, minute, second): Para almacenar horas.
  • Timestamp(year, month, day, hour, minute, second): Para almacenar timestamps (una fecha con su hora).
  • DateFromTicks(ticks): Para crear una fecha a partir de un número con los segundos transcurridos desde el epoch (el 1 de Enero de 1970 a las 00:00:00 GMT).
  • TimeFromTicks(ticks): Similar al anterior, para horas en lugar de fechas.
  • TimestampFromTicks(ticks): Similar al anterior, para timestamps.
  • Binary(string): Valor binario.
Documentación
En Python si el primer estamento de la definición del objeto es una cadena, esta se asocia a la variable __doc__ automáticamente.
def haz_algo(arg):
 '''Esta función es para...'''
 print arg # por poner algo

print haz_algo.__doc__
help(haz_algo)
Pruebas
Pruebas unitarias, pruebas de integración y pruebas de regresión.
La solución más extendida para las pruebas unitarias en el mundo Python es unittest, a menudo combinado con doctest para pruebas más sencillas.
Doctest
doctest permite combinar las pruebas con la documentación.
En 'modulo1.py':
def cuadrados(lista):
 '''Calcula el cuadrado de los numeros de una lista
  >>> l = [3, 2, 1, 0]
  >>> cuadrados(l)
  [9, 4, 1, 0]
  
  >>> tirar = l.pop(3) # tenemos que asignar el valor a algo para que la prueba no finalice aquí
  >>> l.append(4)
  >>> cuadrados(l) == [9, 4, 1, 16]
  True
 '''

 return [n ** 2 for n in lista]

if __name__ == '__main__':
 import doctest
 doctest.testmod() # se le puede indicar el módulo a evaluar (por defecto, el actual)



En 'modulo2.py':
def cuadrado(num):
 '''Calcula el cuadrado de un numero.
  >>> l = [0, 1, 2, 3]
  >>> for n in l:
  ...  cuadrado(n)
  0
  1
  4
  9
 '''

 return num ** 2



En 'validar.py':
import doctest, modulo1, modulo2
doctest.testmod(modulo1)
doctest.testmod(modulo2)
unittest / PyUnit
import unittest

def cuadrado(num):
 '''Calcula el cuadrado de un numero.'''
 return num ** 2

class EjemploPruebas(unittest.TestCase):
 def test(self):
  l = [0, 1, 2, 3]
  r = [cuadrado(n) for n in l]
  self.assertEqual(r, [0, 1, 4, 9])

if __name__ == '__main__':
 unittest.main()
Consejos
No usar 'from module import *' e intentar no usar 'from module import name1, name2'. En su lugar usar:
import module
module.name1...


Compare:
# ugh! 
return dir+"/"+file 
# better 
return os.path.join(dir, file) 
More useful functions in os.path: basename(), dirname() and splitext().



No usar backslash para continuar instrucciones (un espacio añadido al final puede cambiarlo todo); en su lugar encerrar todo entre paréntesis y simplemente partir la línea en 2:
mal:
value = foo.bar()[’first’][0]*baz.quux(1, 2)[5:9] \ 
 + calculate_number(10, 20)*forbulate(500, 360) 
bien:
value = (foo.bar()[’first’][0]*baz.quux(1, 2)[5:9] 
 + calculate_number(10, 20)*forbulate(500, 360)) 
lxml
Conceptos básicos
from lxml import etree
Construyendo un XML:
raiz = etree.Element("root")
print raiz.tag # etiqueta XML
raiz.append(etree.Element("child1"))
hijo2 = etree.SubElement(raiz, "child2") # más eficiente que lo anterior
hijo2.set("id", "002")
print hijo2.get("id")
etree.SubElement(raiz, "child3", id="003", tipo="hijo") # podemos ignorar el valor devuelto e indicar atributos
print(etree.tostring(raiz, pretty_print=True)) # serializamos el XML
atributos = raiz[1].attrib # diccionario con los atributos
print atributos
Los elementos funcionan casi como listas Python:
print len(raiz) # nº de nodos hijos
raiz.append(etree.Element("child3")) # añade un nodo al final (aunque es más eficiente 'etree.SubElement()')
raiz.insert(0, etree.Element("child0")) # creamos un nuevo nodo en la primera posición
# los nodos no se pueden copiar, solo mover (a no ser que utilicemos deepcopy: from copy import deepcopy)
raiz.insert(1, raiz[2]) # esto mueve 'child2' antes que 'child1'
raiz[0] = raiz[-1] # esto mueve 'child3' a la primera posición "sobresscribendo" 'child0'
print [etree.tostring(n) for n in raiz]
Moverse por el árbol:
raiz[0].getparent()   # raiz
raiz[1].getprevious() # raiz[0]
raiz[0].getnext()     # raiz[1]
Texto en nodos:
nieto1 = etree.SubElement(hijo1, "grandchild") # ahora agregamos un nodo después del texto
nieto1.text = "texto"
nieto1.tail = "nieto de hijo1" # añade el texto después del nodo (no dentro)
hijo2 = raiz[1]
nieto2 = etree.SubElement(hijo2, "grandchild", descripcion="hijo de hijo2") # ahora agregamos un nodo después del texto
print etree.tostring(hijo1)
print hijo1.text # solo entrega el texto hasta que aparezca otra etiqueta
print etree.tostring(hijo1, method="text") # nos saltamos las etiquetas
Tree iteration:
print [nodo.tag for nodo in raiz.iter()] # todo el árbol
print [etree.tostring(nodo) for nodo in raiz[2].iter()] # solo la rama del tercer nodo
print [etree.tostring(nodo) for nodo in raiz.iter('grandchild')] # solo los nodos con esa etiqueta
Nodos especiales:
raiz.append(etree.Entity("#234"))
raiz.append(etree.Comment("comentario"))
print [nodo.text for nodo in raiz.iter(tag=etree.Entity)]
print [nodo.text for nodo in raiz.iter(tag=etree.Comment)]
ElementPath (en algunos sentidos hace lo mismo que iterar filtrando por tag):
raiz.find('grandchild')    # primer hijo directo con ese tag
raiz.find('.//grandchild')   # primer descendiente con ese tag
raiz.find('grandchild[@descripcion]') # primer hijo directo con ese tag
raiz.findall('.//grandchild')   # lista con todos los descendientes con ese tag
raiz.iterfind('.//grandchild')  # iterador de todos los descendientes con ese tag
raiz.findtext('grandchild')   # .text del primer hijo directo con ese tag
La clase ElementTree
Es como un documento que contiene, además de un nodo raíz, comentarios, DOCTYPE... Por tanto si 'arbol' es de ese tipo (p.e. porque es el parseado de un fichero), no existirá 'arbol.tag', sino 'arbol.getroot().tag' ya que el nodo raíz es solo una de las posibles partes del árbol.
Parseado de cadenas
'fromstring()' y 'XML()' parsean una cadena de texto generando un 'Element' XML.
'parse()' parsea objetos tipo fichero y URLs generando un 'ElementTree' (no un Element). I.e. Genera un documento completo incluyendo comentarios, DOCTYPE...
import StringIO
s = ' ]>&tasty;'
arbol = etree.parse(StringIO.StringIO(s))
print 'arbol:\n' + etree.tostring(arbol, pretty_print=True)
rama = etree.XML(s)
print 'rama:\n' + etree.tostring(rama, pretty_print=True)



Sustituyendo al parser por defecto:
parser = etree.XMLParser(remove_blank_text=True) # this creates a parser that removes empty text between tags
root = etree.XML("            ", parser)



Parseado dirigido por eventos:
some_file_like = StringIO.StringIO("data1data2data3")
for event, element in etree.iterparse(some_file_like):
    print("%5s, %4s, %s" % (event, element.tag, element.text))

some_file_like = StringIO.StringIO("data1data2data3")
for event, element in etree.iterparse(some_file_like, events=("start", "end")):
    print("%5s, %4s, %s" % (event, element.tag, element.text))
   
# para parsear un XML grande tipo DUMP
some_file_like = StringIO.StringIO("data1data2data3")
for _, element in etree.iterparse(some_file_like, tag="a"):
    print("%4s, %s" % (element.tag, element.text))
    element.clear()
ooolib
Es un proyecto abandonado y sustituido por odslib-python, pero perfecto para leer y escribir hojas de cálculo OpenOffice (ODS).
 # código que muestra como se lee y escribe un ODS llamado 'ejemplo.ods'
 import ooolib
 libro = ooolib.Calc()
 libro.load('ejemplo.ods')

 # recorremos todas las hojas
 for i in range(0, libro.get_sheet_count()):
  libro.set_sheet_index(i) # nos situamos en la hoja
  print libro.get_sheet_name()
  
  # obtenemos la esquina inferior derecha del rectángulo que contiene a los datos 
  (num_cols, num_filas) = libro.get_sheet_dimensions()
  
  # escribimos un decimal en la primera columna justo debajo de la última fila
  libro.set_cell_value(1, num_filas + 1, 'float', 3.14159)
  num_filas += 1 # actualizamos el nº de filas
  
  libro.save('ejemplo.ods') # grabamos a disco los cambios
  
  for y in range(1, num_filas + 1):
   for x in range(1, num_cols + 1):
    # tupla con el tipo de dato (string, float...) y su valor
    dato = libro.get_cell_value(x, y)
    if dato == None:
     print '\tNone' # no hay nada en la celda
    else:
     print '\ttipo:', dato[0], ',,', 'valor:', dato[1]



web2py
Es un framework para programación web. La mejor guía es el PDF oficial (http://www.web2py.com/book). Lo que sigue son puntualizaciones mías o cosas recurrentes para mí.
Un select() devuelve un conjunto de registros (Set) lleno o vacío (len()=0 o not, pero no None). Si cogemos uno solo de ellos mediante un iterador (o añadiendo .first() a select()) obtenemos un objeto Row o None.
Chuleta:
  • rows.as_list() # list of dictionaries
  • rows[0].as_dict() # row.as_dict()
  • rows3 = rows1 & rows2 # union
  • rows3 = rows1 | rows2 # union removing duplicates
  • for row in rows.sort(lambda row: row.name):



Cuenta email externo en Gmail

Si tenemos una cuenta en Gmail y otra en otro proveedor, y queremos usar la interfaz de Gmail para ambas (no solo para recibir, sino también para enviar), podemos configurar Gmail con cuenta Pop3 adicional.

A continuación pongo como se haría para añadir una cuenta del servicio de correo básico de Strato:

  • En Gmail ir a Settings -> Accounts -> Add a POP3 mail account you own
  • Indicar dirección de correo (puede ser el alias)
  • Para recibir correo:
    • el usuario es el email (no el alias)
    • servidor: pop3.strato.com
    • puerto: 995
    • cifrado SSL
    • no tratar como alias
  • Para enviar correo:
    • el usuario es el email (no el alias)
    • servidor: smtp.strato.com
    • puerto: 465
    • cifrado SSL
    • no tratar como alias

Si os saltáis algún paso puede que funcione pero con cosas como que el remitente ponga "enviado desde gmail en nombre de ...".

Una vez configurado, al escribir un correo, debajo del destinatario os aparecerá el remitente para que escojáis entre Gmail o el otro.

Parece que no podéis enviaros correos a vosotros mismos (desde Gmail al otro), y que la frecuencia de chequeo por Pop no es configurable (¿la aumenta si detecta que hay bastante actividad y la disminuye en caso contrario?).

Por último, al recibir un correo dirigido a la otra cuenta, si le dáis a responder, por defecto la respuesta se envía desde Gmail, por lo que tenéis que acordaros de cambiar el remitente.

lunes, 28 de noviembre de 2011

Nociones de PHP

Esto es un resumen que hice hace un par de años en mi primer contacto con PHP:

* La sintaxis es tipo C.
* Las páginas tienen extensión '.php', empiezan por '' y acaban por '?>'.
* Todas las variables son por referencia y todas tiene el prefijo '$' (p.e. $num). Si se antepone el prefijo '&' (p.e. &$num) accedemos al puntero de la variable.
* No es necesario declarar la variables explícitamente, aunque puede ser buena idea hacerlo con los arrays.
* Por defecto solo se puede acceder a las variables locales. Para acceder a una variable global hay que anteponer 'global' (p.e. global $num).
* Las constantes (p.e. const max_num) sí pueden ser globales, pero solo pueden ser tipos simples.
* El comparador de igualdad es '==' aunque se puede usar también '===' que además fuerza a que los elementos comparados sean del mismo tipo (idem con '!=' y '!==').
* Para imprimir un texto en pantalla se utiliza 'echo()' (p.e. echo('Hola.\n');).
* Las cadenas pueden representarse entre comillas simples o entre comillas dobles. Las primeras son literales, mientras que las segundas realizan sustitución de contenido. P.e. si tenemos una variable $texto con el valor 'hola', echo('Prueba: $texto'); devuelve 'Prueba: $texto', mientras que echo("Prueba: $texto"); devuelve 'Prueba: hola'.
* El operador de concatenación de cadenas es el punto '.' por lo que el ejemplo anterior se podría haber escrito como echo('Prueba: ' . $texto);.
* El "if compacto" tiene esta forma: (evaluación ? parte cierta : parte falsa). P.e.: 'echo($num < 0 ? -1 : $num);' es equivalente a 'if ($num < 0) echo(-1); else echo($num);'.
* Ejemplo de asignación de valor por defecto a los parámetros de las funciones: 'function f1($num = -1) {...}'.
* Ejemplo de definición de clase: 'class Clase1 { private static $my_write = NULL;...}'. Ejemplo de uso de clase: 'Clase1::$my_write...'. Ejemplo de uso de clase llamándose a si misma: 'self::$my_write...'.
* Los arrays son muy flexibles. Son más bien árboles que pueden contener elementos de tipo y número diferente. Además se puede acceder a los elementos por índice o por nombre, y los índices no tienen por qué ser consecutivos.
* 'print_r()' recorre un array e imprime cada elemento junto con el nombre de su índice.
* Ejemplo de impresión de array: $tabla = array('cero' => 0, 1 => 'uno', 8 => 'ocho', 'doce' => 12); $tabla[3] = 'tres'; print_r($tabla);
* 'extract()' lee las partes de un array y genera automáticamente tantas variables como elementos con el nombre y valor correspondiente. Ejemplo para el array anterior: 'extract($tabla); echo($doce);'.
* Para depuración de errores por pantalla puede ser muy útil 'debug_print_backtrace()'.
* Para acceder a una BDD MySQL sin ODBC se debe instalar el paquete 'php5-mysql'. Ejemplo de uso: '$conn = new mysqli($host, $username, $password);'.

jueves, 24 de noviembre de 2011

Añadir acción al menú contextual en Windows

La aplicación GNU 'gzip.exe' es de consola de comandos y por comodidad he creado la acción 'Comprimir APK a gz' que ejecuta 'gzip -k' sobre ficheros del tipo APK:
  1. Copiamos 'gzip.exe' a 'C:\WINNT' (es un buen sitio porque está en los "Path")
  2. En una carpeta cualquiera, abrimos 'Herramientas -> Opciones de carpeta'
  3. En 'Tipos de archivo' añadimos una nueva extensión (si no existe ya)
  4. En 'Opciones avanzadas' añadimos una nueva acción con:
  • Acción: Comprimir APK a gz
  • Aplicación: C:\WINNT\gzip.exe -k "%1"

martes, 27 de septiembre de 2011

Conferencia Empresas de Software Libre

El sábado 24 asistí a una conferencia de empresas de Software Libre. Tres empresas españolas explicaron cómo hacen dinero desarrollando software libre.

Todas ellas coincidieron en que la mayoría de sus proyectos tienen una parte de software privativo, bien por conveniencia a la hora de ciertas negociaciones, bien por facilitar convencer a los directivos o bien porque en ese momento es la pieza clave que les hace ser competitivos (en ese caso suele liberarse el código más adelante).

Los tres coincidieron en resaltar que lo importante es el modelo de negocio, entender quién eres como empresa y no el software libre en sí, que lo veían más como una herramienta útil, que como un modelo ético.

  • Javier Viñuales, Yaco
    • Destacó la importancia de una buena red de contactos para conseguir empleados, asesoramiento... Para mantener dicha red hay que contribuir a la comunidad publicando código en proyectos libres.
  • Alvaro del Castillo, Andago
    • Comenzó con un vídeo de Stallman sobre el que comentó que no puede usarse como presentación ante nadie, aunque sus ideales son muy importantes para no perder el norte en cuanto a la libertad del software. Participan mucho en concursos específicos de software libre, en los que se cobra por la implantación y soporte.
  • Juan José Sanchez, Igalia
    • No pudo asistir y en su lugar hablaron dos compañeros suyos. Este empresa tiene una peculiaridad sorprendente: es completamente horizontal. Todas las decisiones importantes se toman en asamblea donde todos los empleados tienen un voto (tanto los recién llegados como los socios fundadores), todos los empleados que están en la misma fase cobran exactamente lo mismo y todos ellos llegan a ser socios en la última fase. Esto les obliga a aumentar sus ingresos constantemente, para lo cual se han internacionalizado de forma bastante abrupta y procuran involucrarse en proyectos muy especializados, que pocas empresas puedan realizar y por los que las grandes multinacionales pagan grandes cantidades.

viernes, 14 de enero de 2011

DNI Electrónico en GNU/Linux Ubuntu

Esta é unha guía para por a funcionar o DNIe en sistemas baseados en Ubuntu. En concreto probeino en Galinux 9.10 e Trisquel Awen 3.5 (ambos baseados en Ubuntu 9.10 Karmic Koala de 32bits).

A maioría dos tutoriales que encontrei son de cando non existía software DNIe para Karmic e tíñase que empregar o software de Jaunty Jakalope. Agora xa hai software para Karmic. Funcionoume cos dous.

Básicamente hai que dar tres pasos:
  1. Instalar o software do lector
  2. Instalar o software do DNI Electrónico
  3. Configurar o navegador Web Mozilla Firefox (ou baseado nel)

Paso1
Enlace
Instalamos por este orden:
  1. libopensc2_0.11.7-7_i386.deb
  2. opensc_0.11.7-7_i386.deb
Estes paquetes están en http://www.dnielectronico.es/descargas/PKCS11_para_Sistemas_Unix/1.4.6.Ubuntu_Jaunty_32/Ubuntu_Jaunty_opensc-dnie_1.4.6-2_i386.deb.tar

Agora hai que indicar ó Synaptic que bloquee estes paquetes para que non se actualicen, senon deixará de funcionar.



Paso2

Instalamos:
  • opensc-dnie_1.4.7-1_i386.deb
Este paquete está en http://www.dnielectronico.es/descargas/PKCS11_para_Sistemas_Unix/1.4.7.Ubuntu_Karmic_32/Ubuntu_Karmic_opensc-dnie_1.4.7-2_i386.tar

Nota: nun dos sistemas tiven que engadir os seguintes paquetes para que funcionara todo:
  • pcscd
  • pcsc-tools (creo que esto solo é necesario se queremos usar o comando de consola 'pcsc_scan' para verificar o funcionamento do lector)
Agora podemos facer unhas comprobacións executando nunha consola 'pcsc_scan':
  • conectamos o lector ó PC e debe aparecer a súa identificación na consola
  • introducimos o DNIe no lector e debe aparecer a súa identificación na consola



Paso3
  • Executamos 'Aplicacións > Ofimática > Rexistrar módulo DNIe PKCS#11'.
  • Comprobamos que se instalou o certificado no navegador Web en 'Editar > Preferencias > Opcións avanzadas > Cifrado > Ver certificados > Autoridares > DIRECCION GENERAL DE LA POLICIA'.
  • Instalamos o dispositivo de seguridad no navegador Web en 'Editar > Preferencias > Opcións avanzadas > Cifrado > Dispositivos de seguranza > Cargar'. Indicamos como nome "DNIe PKCS#11", e como ruta '/usr/lib/opensc-pkcs11.so'.
  • Reiniciamos o navegador Web e volvemos a 'Dispositivos de seguranza' para comprobar que agora un dos 'Virtual Slot' aparece como 'DNI electrónico (PIN1)' ó meter o DNIe no lector. Se pinchamos nel pediranos o PIN que nos entregaron na policía.
  • Agora xa podemos usar o DNIe para identificarnos nas Web que o implementen. Esta páxina é so para verificar que a identificación co navegador Web funciona: http://www.dnielectronico.es/como_utilizar_el_dnie/verificar.html


Tutoriais:
  • http://nauj27.com/blog/2009/11/17/dnie-en-ubuntu-9-10-karmic-koala/
  • http://bitelia.com/2009/12/como-instalar-un-lector-de-dnie-en-ubuntu-9-10
  • http://monkiki.wordpress.com/2010/02/09/instalacion-y-uso-del-dni-electronico-en-ubuntu/
  • http://www.agnix.org/?q=node/2493



Utilidades:

viernes, 10 de diciembre de 2010

Cambio a Pepephone

Me acabo de cambiar a Pepephone para poder tener una tarifa sin establecimiento de llamada.

Yo hago muchas llamadas cortas y otras muchas son cortas lo quieras o no (sale un buzón de voz, un fax, se corta nada más descuelgan, no se escucha nada al otro lado...). No me gusta pagar 0,15€ cada vez que sucede esto. Además si no pagas el establecimiento puedes hacer más llamdas del tipo "Dónde estás" por el mismo precio.

La tarifa que permite esto es la Tarifa Movilonia (http://www.pepephone.com/tarifamovilonia/).

0,12€ por minuto puede parecer caro, pero si la llamada se alarga pasa a 0,07€/min. con lo que comparado con una tarifa constante de 0,07€/min. + 0,15€ de establecimiento, sale más o menos igual (http://www.movilonia.com/tarifa-movil-sin-establecimiento-llamada-pepephone-movilonia-vip/comparativas/).

La configuración es la siguiente:
  • Internet: APN=gprsmov.pepephone.com
  • SMS: Centro de servicio=+34607003110
  • MMS: APN=mms.pepephone.com, usuario=wap@wap, Contraseña=wap125, IP Proxy= 212.073.032.010, Puerto Proxy=80, Servidor de Mensajes=http://mms.pepephone.com/servlets/mms
  • En el panel de control Web se puede solicitar que nos envíen la configuración anterior a nuestro teléfono.
  • Activar llamadas perdidas: primero desactivamos el buzón de voz y todos los desvíos con ##002# y tecla de llamada o envío. Finalmente **62*603000603# y tecla de llamada o envío.
El servicio de llamadas perdidas es al contrario que Simyo: da de menos. Solo avisan de las llamadas recibidas mientras estamos fuera de cobertura o con el teléfono apagado. P.e. cuando alguien nos llama mientras estamos hablando no nos mandan un SMS de aviso al acabar la conversación.

Se puede pedir a Atención al Cliente (1212 ,, 902337373 ,, 634501212 ,, pepephone@pepephone.com) que nuestro teléfono de fuera de cobertura cuando nos llaman mientras estamos hablando (en lugar de dar comunicando), en cuyo caso sí nos llegaría un SMS avisando. Me da la impresión de que es una especie de apaño no fiable y no hay ningún código USSD para hacerlo uno mismo.

martes, 16 de marzo de 2010

Script Bash de copia de seguridade de Gnu a Samba

# Copyright 2010 Adrián Chapela, Manuel Cameselle
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved. This file is offered as-is,
# without any warranty.

# ==============================================================
# Copia de seguridade GNU de disco local a unidade de rede Samba v1.1
# ==============================================================
#
# O script, copia DIR_CONFIGURACION e copia comprimidos DIR_DOCUMENTOS, DIR_CORREO e DIR_ESCRITORIO por rede
# mediante Samba a //SMB_SERVER/SMB_SHARE. Suponse que o usuario ten como login 'usuario1', que existe o directorio
# local '/mnt/snap', que o servidor Samba chámase 'snap' que ten unha carpeta no raíz chamada 'backups' e que
# o acceso é mediante un usuario con login 'usuario_samba' con permiso de acceso total a dito subdirectorio.
# Na variable 'limite_copias' indícase a antigüedade máxima das copias (eliminaránse as que excedan).
# Suponse que o script executarase dende a propio directorio no que está. Se non hai que cambiar 'LOG_FILE'.
# Ó acabar a copia ordéase o apagado do equipo.
#
# O uso recomendado é:
# 1.- Pechar a sesión gráfica (para asegurar que non perdamos nada cando apaguemos o equipo)
# 2.- Lanzar o script
# Para non ter problemas de permisos, podemos lanzar o script como root, pero non convén deixar aberta a sesión de root,
# así que hay que bloqueala ou pechala sen que o proceso de copia morra. Para facer isto nunha sesión de texto:
# 1.- Entramos como root
# 2.- executamos 'screen'
# 3.- Lanzamos o script (p.e. ./copia_seguridade_gnu_a_samba.sh usuario1)
# 4.- Saímos de screen con 'CTRL+a d'
# 4.- Saímos da sesión de root con 'exit'

#!/bin/bash
# Copia de seguridade

#Recibe o nome (login) do usuario do que queremos facer a copia.

#Configuracións USUARIO
usuario=$1
DIR_DOCUMENTOS=/home/$1/Documentos
DIR_CORREO=/home/$1/.mozilla-thunderbird
DIR_ESCRITORIO=/home/$1/Escritorio
DIR_CONFIGURACION=/etc


#Posible mellora: xerar un array de directorios para poder engadir ou quitar directorios fácilmente
#Posible mellora: xerar un array de directorios/ficheiros para excluir da copia

#Ficheiros LOG
LOG_FILE=/var/log/$0.log
#OLLO: o script debe executarse dende o directorio onde está, porque senón $0 collería a ruta completa...
ERROR_LOG_FILE=/tmp/erro

MOUNT_ERROR=/tmp/mount_error

# Samba datos
SMB_USER=administrador
#Se non cubrimos a password (recomendado) pedirase por consola...
SMB_PWD=
SMB_MOUNT=/mnt/snap
SMB_SERVER=snap
SMB_SHARE=backups

# Variables de control
MONTADO=0
fin=0
x=0
num=10
cantidade_copias=0
limite_copias=5

#########
#Funcións
#########
function imprime {
#imprime en pantalla o texto e ademais o engade a ficheiro

echo $1 #se queremos recibir unha data aquí ten que estar nun formato "compacto" coma `date +%d-%m-%Y_%H:%M:%S`
echo $1 >>$2
}

function copia {
#copia o contido indicado (opcionalmente con compresión)
#Parámetros:
# $1 -> nome do directorio destino (doc, escritorio, correo, etc)
# $2 -> ruta do directorio orixe (DIR_DOCUMENTOS, DIR_CORREO, DIR_ESCRITORIO)
# $3 -> tipo de copia (f=tar.gz, d=directorio)

if [ ! -d "$SMB_MOUNT/clientes/$usuario/$1" ]; then
imprime "O directorio $SMB_MOUNT/clientes/$usuario/$1 non existía -> creámolo." $LOG_FILE
mkdir $SMB_MOUNT/clientes/$usuario/$1
fi

imprime `date +%d-%m-%Y_%H:%M:%S`" - Copiando '$1': '$2'..." $LOG_FILE
if [ $3 = "f" ]; then
tar cvfz $SMB_MOUNT/clientes/$usuario/$1/$1-`date +%d-%m-%Y`.tar.gz $2 2> $ERROR_LOG_FILE
elif [ $3 = "d" ]; then
mkdir $SMB_MOUNT/clientes/$usuario/$1/$1-`date +%d-%m-%Y`
cp -vRu $2/* $SMB_MOUNT/clientes/$usuario/$1/$1-`date +%d-%m-%Y`/ 2> $ERROR_LOG_FILE
fi
cat $ERROR_LOG_FILE >> $LOG_FILE
imprime `date +%d-%m-%Y_%H:%M:%S`" - '$1' copiado." $LOG_FILE

#Cantidade de copias
cantidade_copias=`ls -l $SMB_MOUNT/clientes/$usuario/$1/ |grep $1- |wc -l`
if [ $cantidade_copias -gt $limite_copias ]; then
imprime `date +%d-%m-%Y_%H:%M:%S`" - Cantidade de copias de '$1' maior ao límite. Borrando antigas..." $LOG_FILE
cantidade_copias=0
rm -rf `find $SMB_MOUNT/clientes/$usuario/$1 -type $3 -mtime +$limite_copias` 2>> $LOG_FILE
fi
}

#################
#Script principal
#################
imprime `date +%d-%m-%Y_%H:%M:%S`" - Iniciando aplicación..." $LOG_FILE

if [ "$1" != "" ]; then
while [ $MONTADO -lt 1 ]; do
if [ "$SMB_PWD" != "" ]; then
mount -t smbfs -o username=$SMB_USER,password=$SMB_PWD //$SMB_SERVER/$SMB_SHARE $SMB_MOUNT &> $MOUNT_ERROR
else
echo "Introduza contrasinal de '"$SMB_USER"' en '"$SMB_SERVER"'"
mount -t smbfs -o username=$SMB_USER //$SMB_SERVER/$SMB_SHARE $SMB_MOUNT &> $MOUNT_ERROR
fi

cat $MOUNT_ERROR >> $LOG_FILE

MONTADO=`mount |grep //$SMB_SERVER/$SMB_SHARE| wc -l`

if [ $x -gt $num ]; then
MONTADO=1
fi

sleep 5

let x=x+1

if [ $x -gt "1" ]; then
imprime "Reintento "$x $LOG_FILE
imprime "########################" $LOG_FILE
fi
done

if [ $x -lt $num ]; then
if [ ! -d "$SMB_MOUNT/clientes/$usuario" ]; then
imprime "O directorio $SMB_MOUNT/clientes/$usuario non existía -> creámolo." $LOG_FILE
mkdir $SMB_MOUNT/clientes/$usuario
fi

#Documentación, proxectos variados (tar.gz)
if [ "$DIR_DOCUMENTOS" != "" ]; then
copia "doc" $DIR_DOCUMENTOS "f"
fi

#Correo electrónico (tar.gz)
if [ "$DIR_CORREO" != "" ]; then
copia "correo" $DIR_CORREO "f"
fi

#Carpeta Escritorio (tar.gz)
if [ "$DIR_ESCRITORIO" != "" ]; then
copia "escritorio" $DIR_ESCRITORIO "f"
fi

#Configuración equipo (copia directorio sen compresión)
if [ "$DIR_CONFIGURACION" != "" ]; then
copia "etc" $DIR_CONFIGURACION "d"
fi

umount $SMB_MOUNT
else
imprime `date +%d-%m-%Y_%H:%M:%S`" - Erro montando Samba - Anulamos copia." $LOG_FILE
fi

imprime `date +%d-%m-%Y_%H:%M:%S`" - Finalizamos aplicación e ordeamos o apagado do equipo..." $LOG_FILE

# Apagamos o equipo
shutdown -h now "Apagado por copia de seguridade"
else
imprime `date +%d-%m-%Y_%H:%M:%S`" - Debe indicar o usuario do que quere facer a copia - Anulamos copia." $LOG_FILE

imprime `date +%d-%m-%Y_%H:%M:%S`" - Finalizamos aplicación." $LOG_FILE
fi

lunes, 15 de marzo de 2010

Conferencia Richard Stallman

El jueves 30 de abril de 2009 (¡sí!, ¡hace casi un año! O:-), Richard Stallman (el mentor del software libre) dio en Vigo dos conferencias en español sobre el software libre y las patentes, respectivamente.

Software Libre:
  • Considera que el software debe ser libre por cuestiones éticas y de bien común para la sociedad.
  • Enumera 4 libertades que desde GNU describen el software libre:
  • Libertad 0: ejecutarlo -> sin restricciones
  • Libertad 1: modificarlo-> estudiarlo y adaptarlo (acceso al código fuente)
  • Libertad 2: copiarlo -> redistribuir copias
  • Libertad 3: distribuirlo -> redistribuir copias modificadas
  • Sin alguna de estas libertades falta, el software no es bueno para la sociedad ya que estamos atados de alguna u otra manera, lo que nos perjudicará a corto, medio o largo plazo.
  • Considera que las razones éticas son las únicas que garantizan el bienestar de la comunidad a largo plazo.
  • Aquellos, como Linus Torvalds, que apoyan al software libre por razones prácticas y no éticas, no hacen bien a la comunidad a largo plazo.
  • Insistió en que Ubuntu, Trisquel, Galinux, Debian, etc. son sistemas operativos GNU con kernel Linux. No le gusta que se llamen Linux ya que esto solo es el núcleo (una pequeña parte) y fue lo último que se añadió. Todo el trabajo hecho durante años por GNU queda marginado. Tampoco considera correcto GNU/Linux ya que equipara ambas cosas.

Patentes:
  • No cumplen con su propósito y en el campo de la informática son totalmente inadecuadas. No entra a valorar si pueden ser adecuadas en otros campos.

Comenzó con estilo serio, aunque "pintoresco", pero tuvo bastantes momentos de humor:
  • Sobre la piratería piensa que no está bien atacar barcos con otros barcos.
  • Sobre la piratería informática piensa que no está bien atacar barcos con ordenadores.
  • Sorprendió con su dominio del español realizando varios juegos de palabras como "Te quiero mucho" (mientras bebía té) o "Por las noches quierote".
  • Al final se disfrazó de fiel de la iglesia "emacs".

Mucho después me enteré de que hace años que escribió un libro (libre, claro :-) sobre estas dos cuestiones. Se puede comprar la edición impresa, descargar la edición digital gratuitamente o ver la edición digital online.

Si no me equivoco volverá a estar en Vigo este 30 de abril de 2010.

jueves, 11 de marzo de 2010

Error VPN en Galinux/Ubuntu 9.10

Na 9.04 a autenticación de 'Certificados (TLS)' funcionaba ben, pero na 9.10 hai que facer o seguinte cambio (apaño):
  • Cambiar o tipo de autenticación de 'Certificados (TLS)' a 'Contrasinal con certificados (TLS)' e ¡inventarse un usuario, contrasinal e contrasinal de chave privada!.

jueves, 4 de marzo de 2010

Mover /home a outra partición

Eu tiña todo o sistema de ficheiros de GNU nunha única partición, pero dinme conta de que iso non era boa idea porque ó restaurar unha imaxe de sistema feita con Clonezilla ¡perdería tódolos datos de /home!. Así que dispúxeme a reducir a partición de / e facer unha nova para o /home no espacio liberado.

arrancar con usb live e:
  • reducir a partición do sistema e crear unha nova para o 'home' (p.e. co editor de particións 'gparted' )
  • acceder ó sistema de ficheiros do DD
  • renomear /home a /home_old
  • crear o directorio /home
  • montar a partición en /home (fstab + mount...)
  • copiar todo o contido de /home_old a /home
Se tes as ideas claras sobre como funciona o sistema de ficheiros de GNU é sinxelo pero as posibilidades de liala son altas.

lunes, 1 de marzo de 2010

Instalación de GNU/Linux

Instalación de Gnu/Linux (Ubuntu 8.04 / Galinux 8.04) no portátil OKI NB 0108.
Tamén se aplica a Trisquel 3.0.

Xa hai unha partición con XP e outra con MSDOS. Ademáis o xestor de arranque GAG está no sector de arranque do DD.

Arrancamos o CD de instalación de Ubuntu/Galinux coas opcións 'noapic' e 'acpi=off', senón colgarase.

Pola contra, noutros equipos pode suceder que o instalador poña 'acpi=off' sen necesidade e polo tanto non funcionarán cousas como apagar o PC. Nese caso probaremos a quitalo.

¡OLLO!: por defecto, a instalación é "Guiada", o que significa que ALTERA as particións sen pedir confirmación co conseguinte risco de perder datos (por desgracia a min xa me pasou e tíven que empregar "Active Partition Recovery" para recuperar "parte" dos meus datos).

Así que nestos casos hai que seleccionar instalación "Manual":
  • Escollemos partición onde queremos que se instale
  • Indicamos que monte /
  • Engadimos unidade de intercambio
¡OLLO!: por defecto GRUB instálase no sector de arranque do DD, co que perderemos o xestor de arranque que teñamos ahí (p.e. GAG). Para cambiar isto, na última "pantalla" do instalador, hai un botón 'Avanzado' no que lle podemos indicar que non instale Grub ou que o instale na propia partición de linux.

Aínda que non é necesario é convinte que non instalemos todo o sistema de ficheiros nunha única partición:
/var/log nunha partición aparte de 2GB garante que o sistema non se colga se queda sen espacio para os logs.
/home nunha partición aparte permite reinstalar o sistema sen perda de datos.

Ó arrancar por primeira vez, debemos engadir a '/boot/grub/menu.lst' as opcións 'noapic' y 'acpi=off'. Se nos olvidamos non volverá a arrancar e tenremos que editar o arranque do Grub (opción kernel) para agregalas, arrancar e finalmene engadilas a 'menu.lst'.

É posible que o noso Linux non detecte a Wifi nin tampouco correctamente o monitor (impidindo usar a resolución máxima). Para o primeiro podemos usar NDisWrapper e usar os drivers de Windows e para o segundo, podemos usar displayconfig-gtk. En Trisquel é mais difícil usar estas ferramentas, xa que non están soportadas ó ter relación con software non libre.
Se con displayconfig-gtk non conseguimos nada haberá que probar a editar directamente '/etc/X11/xorg.conf' e/ou descargar un driver específico para a versión concreta da nosa distribución.

Unha vez finalizada a instalación pode ser útil instalar Metapaquetes como:
-Thunderbird
-geany (tipo Ultraedit)
-SVN Workbench
-MySQL Query Browser
-Bless Hex Editor (editor hexadecimal)
-Wine Microsoft Windows Compatibility Layer
-VirtualBox OSE (para poder instalar Windows para programar con Visual Basic)
-VPN Connection Manager
-Samba (para compartir impresora e administrar grupo de red, etc.)
-Device Manager
-Gparted (Editor de particións de Gnome)
-KGRUBEditor
-Swfdec Flash Player

Tamén pode ser interesante instalar, a través de Synaptic, os paquetes:
* gettext
* p7zip-full
* smbfs
* smbclient

Se non están nos repositorios, estas aplicacións podemos instalalas directamente dende ficheiro (despois de descargar os paquetes correspondentes das súas respectivas Webs):
-peazip_3.0.LINUX.GTK2-2_all.deb. Se non se instala, para ter todas as funcións do formato 7Zip, hai que instalar o paquete 'p7zip-full'.
-GoogleEarthLinux.bin

Configuracións adicionais:
-O terminal gnome aparece moi pequeno por defecto. Para cambialo, utilizamos 'Sistema->Preferencias->Aplicacións Preferidas->Sistema', e engadimos isto ó comando de emulador de terminal: ' --geometry=99x35'.
-gedit -> engadimos o plugin "Panel do navegador de ficheiros" e cambiamos o esquema de cores a 'Oblivion'.
-geany -> agregamos o plugin "File Browser" e engadimos as extensións de VB ó ficheiro ~/.config/geany/filetype_extensions.conf: "FreeBasic=*.bas;*.bi;*.asp;*.frm;". Se as cores de resaltado de sintaxis non gustan, pódese probar a invertilas (Preferences->Editor->Display) ou podemos instalar geany-darkTango en ~/.config/geany/filedefs (e despois creamos ~/.config/geany/filedefs/filetypes.common coas liñas: [styling] ,, default=0xeeeeec;0x2e3436;false;false ,, current_line=0x0;0x565a5b;true;false ,, caret=0xffffff;0x0;false;false ,, brace_good=0x0000ff;0x6AC16A;true;false). Se a versión non arranca en galego, hai quedescargar o ficheiro 'http://i18n.geany.org/gl.po', compilalo con 'msgfmt gl.po', renomear o resultado a 'geany.mo' e copialo a '/usr/share/locale/gl/LC_MESSAGES/'.
-Bless Hex Editor -> cambiar 'default edit mode' a 'Overwrite', senón podemos liala cos prgs...
-Pode ser que as actualizacións automáticas non instalen tódolos recursos de idiomas => ao abrir "Soporte de Idioma" avisaranos se queremos instalalas.
-Para abrir por defecto un tipo de ficheiro con unha aplicación determinada, non chega con escoller 'Abrir con...' do menú contextual, senón que hai que facelo na pestaña 'Abrir con' da fiestra de propiedades.
-Desactivar 'Reproducir sons de alerta' en 'Preferencias -> Son'.
-VitualBox: podemos cargar un disco duro virtual en ~/.VirtualBox/HardDisks/ con Windows precargado (xerado previamente noutro VirtualBox).
-Se algunha aplicación non aparece no idioma preferido, buscaremos se hai unha extensión localizada (p.e. 'paquete_de_idioma_galego_para_o_thunderbird_2-2.0-tb.xpi').
-Se a instalación do Thunderbird non o fai, podemos agregalo como aplicación de inicio e 'Sistema->Preferencias->Aplicacións de inicio' con 'thunderbird %u'.



Varios:
-Para configurar OpenVPN intentamos con importar a configuración. Senón funciona escollemos cada ficheiro no sitio correspondente (ta.key en TLS, etc.). En 'Configuración IPv4->Camiños' marcamos 'Usar esta conexión só para recursos na súa rede'.

-Para facer unha imaxe da partición de sistema Clonezilla Live CD (partclone) é unha boa idea. Permite recuperar a imaxe nunha partición igual ou maior (aínda que non nunha menor).

-Se queremos montar automáticamente unha partición NTFS, como root, engadimos:
/dev/sda5 /media/DATOS auto
en /etc/fstab (sudo mkdir /media/DATOS ; sudo nano /etc/fstab)
Se a partición é FAT32 poida que non funcione ben 'auto' e haxa que por algo como 'vfat rw,uid=1000,nodev,user=meu_usuario,utf8'.

-Para saber qué drivers de vídeo temos e os seus nomes 'ls /usr/lib/xorg/modules/drivers/' devolve ficheiros como 'sisusb_drv.so', o que quere dicir que o nome do driver é 'sisusb'.

-Se eliminamos kernels antiguos VirtualBox pode deixar de funcionar con erro: "Kernel driver not installed (rc=-1908).... reinstall the kernel module by executing '/etc/init.d/vboxdrv setup'", pero pode non existir (p.e. versión repositorio Ubuntu14.04) e se soluciona con ' sudo dpkg-reconfigure virtualbox-dkms' (¿tamén necesario ' sudo dpkg-reconfigure virtualbox'?)


-Software non libre habitual:
  • plugin Flash de Adobe
  • drivers propietarios de NVidia
As distribucións GNU libres, como Trisquel (http://trisquel.info) usan unha versión libre do kernel Linux exenta de drivers propietarios e algunhas aplicacións modificadas (p.e. Firefox) para que non acepten fácilmente extensións non libres. Ademais nos seus repositorios só hai aplicacións que estén confirmadas que sexan libres.
-Galinux é unha galeguización de Ubuntu mediante o paquete AraOS (parece que mediante este paquete calquera pode facer unha distribución Ubuntu galeguizada). Igual que Ubuntu non é 100% libre.
Semella que o proxecto está abandoado pola actual Xunta, no obstante actualízase coma se fose Ubuntu.
A URL oficial é 'http://galinux.mancomun.org/' aínda que o mais interesante está en 'http://wiki.mancomun.org/'.


Migración dende Windows:
=================
Se estamos a migrar dende Windows a GNU e queremos conservar cousas, antes de instalar GNU:

-Exportar as conexións ODBC con REGEDIT co formato REGEDIT4.
-Instalar Thunderbird en Windows e importar todo o correo e configuracións (http://www.howtoforge.com/importing_outlook_express_into_thunderbird_evolution). Aínda que queramos utilizar Evolution en GNU hai que pasar previamente por Thunderbird.
-ó rematar teremos os contactos en '...Outlook Express'. É aconsellable movelos a 'Personal Adress book'.

Despois de instalar GNU:
-Para restaurar o correo electrónico sobreescribiremos os "datos" de Thunderbird de GNU cos de Thunderbird de Windows (http://www.howtoforge.com/importing_outlook_express_into_thunderbird_evolution).
-Para restaurar as conexións ODBC agregamos as librerías ODBC32 y ODBCCP32 no Wine, instalamos MyODBC e despois importamos as conexións co REGEDIT de Wine. Tamén hai que agregar as librerías ODBC32 y ODBCCP32. Quizais haxa que executar './winetricks mdac28'. Para xestionar as conexións ODBC en Wine úsase: .wine/dosdevices/c:/windows/system32/odbcad32.exe.

miércoles, 16 de septiembre de 2009

Simyo

Después de que Yoigo subiera el establecimiento de llamada después de haber dicho que nunca subirían las tarifas (https://www.facua.org/es/noticia.php?Id=4091&IdAmbito=21) y después de otra jugarreta anterior (https://www.facua.org/es/noticia.php?Id=2684) y dado que han dejado de ser los más baratos y dado que es el único operador que tiene consumo mínimo en prepago...... me he pasado a Simyo.
Llamé a Yoigo para solicitar los códigos para liberar los teléfonos (ya habíamos acabado el período de permanencia) y me los dieron.

He pensado también en Eroski Móvil ya que son los únicos que tienen una tarifa sin establecimiento de llamada (aunque PepePhone tiene ofertas puntuales también sin establecimiento) pero no me daban las cuentas con el precio por minuto.

Con Simyo la libertad es total (sin permanencia y sin consumo mínimo, ni siquiera en contrato) y las tarifas son muy buenas. Es una opción excelente para los que ya tienen teléfono liberado. Además no he notado merma de cobertura.

Como defectos: la navegación GPRS es penosa, no contestan por email y su panel de control web es muy lento.

La configuración es la siguiente:
  • Internet: APN=gprs-service.com
  • Internet WAP: APN=gprs-service.com, IP Proxy=217.018.032.181, Puerto Proxy=8080
  • MMS: APN=gprs-service.com, IP Proxy=217.018.032.181, Puerto Proxy=8080
  • SMS: Centro de servicio=+34644109030

Configuración Yoigo

Configuración Yoigo para datos: Internet, SMS, MMS...
  • Cuentas de datos: APN=internet
  • Proxy Internet: IP=010.008.000.036, Puerto=8080
  • Cuentas MMS: APN=mms
  • Proxy MMS: IP=193.209.134.141, Puerto=80
  • Centro de servicio SMS: +34622996111

jueves, 18 de junio de 2009

Hacer la declaración de la Renta (IRPF)

Hacer la declaración de la Renta es toda una aventura por mucho que haya mejorado.

  • Lección nº 1: los borradores casi siempre contienen errores. Algunas veces, graves, por lo que normalmente solo sirven para no tener que introducir todos los datos desde cero.
  • Lección nº 2: los datos importados con el programa Padre, tampoco son fiables al 100%.
  • Lección nº 3: la declaración que te hacen gratuitamente en las delegaciones de la Agencia Tributaria, tampoco es fiable. Además, por mucho que te la haga la propia Hacienda, no tiene ninguna validez legal. Es decir, si contiene errores, legalmente eres tú el único responsable.

En mi caso personal, estas son las cosas que tengo que confirmar siempre:
  1. Que aparezcan mis dos hijos. Si hemos cobrado ayudas por ellos, que aparezca el cobro pero que aparezca también el derecho a cobro, ya que sino ¡el programa lo computaría como que lo tienes que devolver!.
  2. Plan de pensiones. Poner el mío y también el de mi mujer (aunque mi declaración sea individual). Para poder hacerlo hay que comprobar que sus ingresos no superen un límite y asegurarse de que ella no lo declare.
  3. Donaciones: apadrinamiento.
  4. Deducciones autonómicas (Galicia):
  • Alquiler. Hay que comprobar que los ingresos no superen un límite.
  • Cuidado de hijos: gastos de guardería.