martes, 18 de septiembre de 2012

Formula para repartir los mismos puntos en una lista de longitud variable

Enunciado:

Realizas una encuesta con preguntas donde te responde con una lista de longitud variable por orden de preferencia.
Quieres repartir una cantidad fija de puntos entre las respuestas dando mas siempre a las situadas en primer lugar.

Por ejemplo "elija su ciudad favorita". En una respuesta te dicen solo una "CASTELLÓN", otra persona te dice "MADRID", "BARCELONA", "VALENCIA" y una tercera te dice "MADRID", "VALENCIA". "SEVILLA", "BURRIANA"
Hay 100 puntos a repartir, veamos los resultados:
  • La primera respuesta dará a CASTELLÓN 100 puntos
  • La segunda entregará  50 puntos a MADRID, 33 a BARCELONA y 17 a VALENCIA.
  • La tercera entrega a 40 a MADRID,  30 a VALENCIA, 20 a SEVILLA y 10 a BURRIANA
Al final tenemos: CASTELLON (100), MADRID (73), VALENCIA (47),BARCELONA (33) y BURRIANA (10).
Gana CASTELLON a MADRID, pese que CASTELLON solo haya sido votada una vez, y MADRID dos veces y en primer lugar en ambos casos.
Se valora mas cuando solo se da una respuesta que si se responde con varias. La persona que ha votado a CASTELLON está muy convencida, mientras que las demás tienen gustos dispersos.

Se me ocurre aplicarlo en las carreras de los autos locos, los participantes puntúan mas cuantos menos coches acaban. Seria peligroso aplicarlo en la F2 (Fernando Alonso dixit).

Solución

puntuación(r,p)=2*( (r-p+1)/(r*(r+1)))

Donde r es el número de respuestas y p la posición de la respuesta que estamos puntuando.

Comentario 

Esta formula puede implantarse en SQL  o en MDX. Es fácil construir una tabla Excel para ver las puntuaciones.


Cómo puede verse me apoyo en la suma de la serie geometríca.

Archivos duplicados y hash MD5

Enunciado

Quieres detectar y mover a una carpeta todos los archivos duplicados en una carpeta y subcarpetas. 
Cuando hay duplicidados mueves el archivo con nombre mas largo o que está en una subcarpeta.
En la nueva carpeta creas las subcarpetas originales.

Solución:

Para detectar los archivos duplicados usamos el hash MD5 calculado con la clase correspondiente en el framework .NET, recorremos recursivamente las carpetas, comparando archivos  y moviendo el que tiene path completo mas largo.
Const pathdup="c:\duplicados" 'carpeta donde se moveran los duplicados
Sub procesaCarpeta(carpeta As String)
'carpeta: carpeta a recorrer
   Dim existencias As Dictionary(Of String, String)
    Dim Md5 As New MD5CryptoServiceProvider()
        Try
            Dim archivos = My.Computer.FileSystem.GetFiles(carpeta)

            For Each archivo In archivos
                Dim acont = My.Computer.FileSystem.ReadAllBytes(archivo)
                Dim clave = Convert.ToBase64String(Md5.ComputeHash(acont))
                If existencias.ContainsKey(clave) Then
                    If existencias(clave).Length <= archivo.Length Then
                        'mover nuevo
                        My.Computer.FileSystem.MoveFile(archivo, archivo.Replace("C:\", pathdup))
                    Else
                        My.Computer.FileSystem.MoveFile(existencias(clave), existencias(clave).Replace("C:\", pathdup))
                        existencias(clave) = archivo
                    End If
                Else
                    existencias.Add(clave, archivo)
                End If
            Next
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.OkOnly, carpeta)
        End Try

        For Each hija In My.Computer.FileSystem.GetDirectories(carpeta)
            procesaCarpeta(hija)
        Next
    End Sub

Comentario

Puede parecer tonto pero limpié 7 Gb del disco duro de mi casa. La explicación es simple, mi hija antes de editar copiaba todas las imágenes de una carpeta y las pegaba en la misma carpeta o en una  subcarpeta (a veces ambas cosas).

jueves, 23 de agosto de 2012

Interfaz de usuario de Windows 8

Síntomas

Instalas Windows 8, al arrancar te encuentras con una pantalla que parece un Windows Phone.
¿Qué haces a continuación? ¿Donde está botón de inicio? ¿el panel de control? ¿Cómo lo apagas?

Solución:

Para ver el escritorio normal busca un cuadro con margaritas entre todos esos que cambian de color y hablan de fútbol.
Cuando hagas clic en las margaritas llegaras al escritorio.
Pero no encontraras el botón de inicio, porque no lo hay, ni sin duda lo habrá.
Una alternativa al botón de inicio es desplazar el ratón a las esquina superior o inferior  derecha
Se despliega una barra en el lateral derecho, allí tienes la lupa, le das clic y tienes una lista de programas e incluso el panel de control.
Otra opción interesante en la barra que has desplegado es la de configuración (la rueda tipo tiempos modernos abajo). Allí tienes un botón iniciar/apagar que sirve para apagar o REiniciar.
Para cerrar sesión tienes que volver a la ventana de los cuadrados cambiantes (se llama Metro) y pulsar en tu nombre arriba a la derecha.
Pero desde el escritorio ¿cómo vuelves al dichoso Metro?
Hay algo maravilloso en Windows 8, la tecla de Windows  te lleva a Metro sacándote de cualquier embrollo donde te hayas metido sin encontrar la flecha de salida.
¡Es como la tecla escape pero con dibujos!

También sigue funcionando esta tecla con la R para ejecutar un programa. Es una lástima que no sirva también para desinstalar Windows 8 y volver al 7.

Comentario:

Microsoft ha vuelto a cometer los errores del Vista y del Office 2007: confiar en diseñadores y creativos del marketing que piensan que un ordenador es un producto para consumir (metro) no para crear (escritorio), y que a un teclado le sobran 103 teclas porque les molesta que otros escribamos con todos los dedos.

Las personas normales somos reacias al cambio, nos gusta que la llave o interruptor para arrancar el coche esté en el lado derecho del volante. Aceptamos los cambios cuando nos aportan algún beneficio.
En mi Nissan, no me molesta presionar el pedal de freno al mismo tiempo porque la llave inteligente me evita rebuscar en los bolsillos (ante guardaba la llave nada mas abrir la puerta del coche y luego no la encontraba).

Pero ¿qué aporta Metro? ¿El mismo interfaz en todos mis dispositivos?
¡Pero si yo cambié los teléfonos móviles de mi empresa desde Windows Phone a Android cuando salió Windows Phone 7.0 precisamente con ese interfaz Metro y sus contenidos cambiantes!
Si fuera solo una pantalla de bienvenida pasaría, pero al quitar el botón de inicio pierdes mucho tiempo cada vez que quieres hacer algo normal.
Lo peor es que cuando salio la preview algunos programadores sacaron utilidades para recuperar ese botón de inicio, pero desde Microsoft alguien ordenó que bloquearan esta posibilidad. Sostenella  y no enmendalla

Desde el punto de vista de empresa no anticipo nada bueno a Windows 8:
  1. Costes de formación usuario final. ¿os acordaís del botón de office 2007 y los usuarios queriendo imprimir?.
  2. La pantalla inicial que ofrece juegos, musica, videos, noticias de futbol, cualquier cosa menos productividad. (y eso que estoy probando la versión enterprise).
El caso es que por lo que he leído y experimentado en Windows 8 hay mucho trabajo de core que agiliza el sistema en el arranque y en el uso. Es una lastima que todo ese trabajo se desaproveche y espero que Microsoft corrija antes de que sea tarde como ya hizo con el Windows 7 y otros muchos productos antes.

Yo he usado Windows en todas las versiones tanto de escritorio ( 1, 2, 2.1, 386,3,0, 3,1,  3.11, 95, 98, me, XP,  Vista, Windows 7) y como de servidor (OS/2 con IBM, NT 3.1, NT 3.5, NT 3.51, NT4, 2000, 2003, 2003R2, 2008, 2008R2,...y se de que hablo.
Porque al fin y al cabo a mí me gusta Microsoft,..., aunque en Visual Studio 2012 haya puesto los menús en mayúsculas, ¡otro creativo suelto!.






Se interrumpe la instalación del Rollup 4 para Exchange 2010 SP2

Síntomas:

Cuando aparece la barra de progreso de instalación del RU4 para XCHG2012 SP2 indica que está intentado detener los servicios, inmediatamente después informa que está deshaciendo las acciones y te informa que no se ha instalado.

Solución:

Guarda el archivo msp del RU en una carpeta.
Abre un ventana de símbolo de sistema en modo administrador 
(ya sabes botón derecho en el menú de inicio, ejecutar como administrador)
Ves a la carpeta y ejecuta.

domingo, 19 de agosto de 2012

Script PowerShell para apagar infraestructura vmWare

Enunciado

Quieres que en caso de corte de suministro eléctrico prolongado las maquinas virtuales de tu infraestructura vmWare se apaguen ordenadamente por grupos y dejando para el final las maquinas mas críticas (los servidores de dominio).

Entorno

En mi caso dispongo de un SAI EATON que puede notifica a un software Intelligent Power Protector que puede lanzar un batch.

Solución

Me baso en el post http://www.virtu-al.net/2010/01/06/powercli-shutdown-your-virtual-infrastructure/ y algunos de los comentarios que allí hacen.
Coloco las maquinas que deben apagarse en varias carpetas o grupos según su importancia.
La idea es lanzar el siguiente script PowerShell desde un batch (powershell script.ps1)
if (!(get-pssnapin -name VMware.VimAutomation.Core -erroraction silentlycontinue)) {
     add-pssnapin VMware.VimAutomation.Core
 }
Set-PowerCLIConfiguration -DisplayDeprecationWarnings $false -Confirm:$false 

function ApagaMaquinasCarpeta 
#Ordena el apagado de las $maquinas en un grupo (host, carpeta, cluster,...etc) y espera un $tiempo dado 
{
 write $Carpeta
 $Maquinas=Get-Folder -Name $Carpeta 
 Foreach ($VM in ($Maquinas |Get-VM  )){
      Shutdown del invitado
     $VM | Shutdown-VMGuest -Confirm:$false
 }
 #Bucle esperando que apague
 $Inicio= (Get-Date).TimeofDay
 do {
   sleep 1.0
  $Lapso=(Get-Date).TimeofDay - $Inicio
     $Quedan = $Tiempo - ($Lapso.TotalSeconds)
  write  $quedan
  $numvms = ($Maquinas |  Get-VM | Where { $_.PowerState -eq "poweredOn"} ).count
  write "Esperando que se apaguen $numvms VMs o que transcurran $Quedan segundos"
  } until ((@($Maquinas |  Get-VM | Where { $_.PowerState -eq "poweredOn"} ).count -eq 0) -or ($Lapso).TotalSeconds -ge $Tiempo)
 write "Maquinas apagadas o plazo consumido ($Lapso)"
}
 clear 
$VCenter= "vcenter.empresa.org"
 
Connect-VIServer $VCenter
$Hosts= Get-VMHost
$Tiempo=1
$Carpeta= "Apagado Directo"
ApagaMaquinasCarpeta 
$Tiempo=20
$Carpeta= "Escritorios"
ApagaMaquinasCarpeta 
$Tiempo=300
$Carpeta= "Servidores"
ApagaMaquinasCarpeta 
$Tiempo=300
$Carpeta= "Ultimo apagado"
ApagaMaquinasCarpeta 
$ESXSRV = Get-VMHost
$ESXSRV | Foreach {Get-View $_.ID} | Foreach {$_.ShutdownHost_Task($TRUE)}
Disconnect-VIServer -server $VCenter -Confirm:$false
write "Fin"
#Apago la estación IPP
Stop-Computer

Comentarios

Eaton suministra un modulo para instalar en la infraestructura, pero me gusta mas esta solución porque puedo verla, probarla y guardar un archivo de seguimiento (redireccionando powershell).

Alguien se preguntará el por qué no uso parámetros de función en lugar de variables globales: no funcionaban, tenia prisa y trabajar con mandatos del tipo shutdown me pone muy nervioso.
En primer lugar porque un error te tira todas las maquinas, pero también porque la actualización ESXi 5.0 me había dejado sin módulos de shutdown, en el agosto mas caluroso desde hace años y podían producirse cortes de suministro por el elevado consumo de aire acondicionado.

viernes, 13 de julio de 2012

Descargar un informe desde Report Server en PDF

Enunciado 

Usando Visual Basic.NET, descarga un informe desde Report Server que puede ser utilizado para mandarlo por correo o generar una descarga por web.

Solución

Es tan simple como esto:
Private Function solicitaInformeSSRS(idRespuesta As Integer) As Byte()
        Dim informe As New ServerReport
        With informe
            .ReportServerUrl = New Uri(My.Settings.urlServerReport)
            .ReportPath = My.Settings.SSRSEncuestaPromocion
            .ReportServerCredentials = New MyReportServerCredentials
            .SetParameters(New ReportParameter("idRespuesta", idRespuesta))
            .Refresh()
            Return .Render("PDF")
        End With
End Function
Hay que utilizar la clase  MyReportServerCredentials que puedes encontare en mi post Autentificación Reporting Services

Usos:

Una vez descargado el array de byte, puedes utilizarlo para adjuntarlo en un email como un adjunto:

 msj.Attachments.Add(New Attachment(New System.IO.MemoryStream(informe),"informe.pdf")) 
O entregarlo en el stream de salida de una página (descarga del PDF):
Response.Clear()
Response.ContentType = "Application/PDF"
Response.AddHeader("content-disposition", String.Format("attachment; filename={0}.PDF", "archivo.PDF"))
Response.BinaryWrite(bytes)

Comentario:

Usar informes del servidor resulta mas simple, y no digamos ya si existen subinformes, ver Subinformes RDLC en Report cargado dinámicamente.

miércoles, 11 de julio de 2012

Lanzar eventos desde un control de usuario ASP.NET

Enunciado

Una forma muy cómoda de comunicar un control de usuario (.ascx) con la página u otro control de usuario que le contiene es definir un evento, lanzarlo y recogerlo.

Por ejemplo:
Un control de usuario realiza un búsqueda y la visualiza en un dataGrid.
Cuando el usuario elige un comando en una fila del datagrid el control de usuario lanza un evento informando con un texto y un identificador.

Solución:

Un pequeño truco, en el data grid usamos un comando con nombre y argumento.

   
        
           
         
         
   

En el código del control de usuario definiremos el evento y lo lanzaremos en dos casos:
  1. Con el evento RowCommand del datagrid
  2. Al hacer en click en cancelar (un botón)

Public Event eleccion As System.EventHandler

Private Sub gvActiv_RowCommand(
                        sender As Object, 
                        e As GridViewCommandEventArgs
                        ) Handles gvActiv.RowCommand
        RaiseEvent eleccion(Me, New ArgumentoObjetoElegido(e))
End Sub

Protected Sub LBCancelar_Click(
                        sender As Object,
                        e As EventArgs) Handles LBCancelar.Click
        RaiseEvent eleccion(Me, New ArgumentoObjetoElegido)
End Sub
Observar que como segundo argumento al lanzar el evento (RaiseEvent) hemos usado la clase ArgumentoObjetoElegido que hereda de EventArgs.
Esta clase puede crearse con y sin los argumentos. En caso de usar argumentos estos son del tipo GridViewCommandEventArgs.
Public Class ArgumentoObjetoElegido
    Inherits EventArgs
    Public Property Identificador As Integer = 0
    Public Property Descripcion As String = "(Nada seleccionado)"

    Public Sub New(Argumentosfila As GridViewCommandEventArgs)
        Identificador = CInt(Argumentosfila.CommandArgument)
        Descripcion = Argumentosfila.CommandName
    End Sub
    Public Sub New()
        'sin argumentos, valores iniciales arriba
    End Sub
End Class
Finalmente hay que recoger el evento en la página principal:
Private Sub EligeActiv_eleccion(sender As Object, e As ArgumentoObjetoElegido) Handles EligeActiv.eleccion
        If e.Identificador = 0 Then
            'Codigo para procesar cancelación
            If controlador.ActividadEnCurso.identificador = 0 Then
                controlador.ControlMenos()
            Else
                EstableceVista(VistaActividad)
            End If
        Else
            'codigo para procesar una elección el grid
            controlador.ActividadEnCurso.identificador = e.Identificador
            controlador.ActividadEnCurso.Descripcion = e.Descripcion
            EstableceVista(VistaActividad)
        End If
    End Sub