Existirá una categoría especial denominada "Todos" cuyo código será 0 y que tendrá un significado especial.
En la base de datos de productos se almacenarán los distintos productos, cada uno con el código de la categoría
a la que pertenece, su descripción y su precio de venta (en euros). Para ello usaremos los siguientes tipos:
Cada base de datos residirá en un fichero de texto en el servidor, de modo que contendrá una categoría (o un artículo) por
línea de texto. Desde [aquí] puedes descargar un fichero ejemplo para las categorías
y desde [aquí] otro con los productos.
-
Comienza definiendo una función leerBDCat :: FilePath -> IO BDCat
que, dado un nombre
de fichero que contiene una base da datos de categorías, lea este fichero y devuelva la lista de categorías correspondiente. Define
también una función leerBDProd :: FilePath -> IO BDProd
para leer una base de datos de productos. Recuerda que puedes pasar
de una cadena a un valor de tipo Cat
o Prod
usando la función read
y que puedes obtener las distintas
líneas de un fichero usando lines
:: String -> [String]
.
-
Define una función perteneceA
tal que xs `perteneceA` ys
devuelva True
si
la cadena de caracteres xs
aparece en la cadena ys
. Por ejemplo,
"Hitachi" `perteneceA` "CRT 17 Hitachi 615"
=>> True
-
Define una función tablaProd :: BDProd -> BDCat -> Html ()
que, dadas una base de
datos de productos y otra de categorías, devuelva una tabla HTML con tantas filas como productos tenga la
primera base de datos. Cada fila de la tabla contendrá la descripción de la
categoría del producto (cuidado, NO su código), la descripción del producto y su precio. La tabla debe también
incluir una primera fila que haga de cabecera. En definitiva, la tabla debe ser similar a la que
aparece en la figura de la imagen anterior.
Puedes consultar el fichero Tabla.hs
en los
ejemplos de la librería para ver como se pueden crear tablas HTML.
-
Define una función buscarProd :: String -> BDProd -> BDProd
que, dadas una palabra
y una base de datos de productos, devuelva una nueva base de datos en la que solo aparezcan los
productos que contengan la palabra en su descripción.
-
Define un formulario que contenga una caja de texto (un elemento hTextInput
) y un botón
(un elemento hButtonInput
), de modo que, al pulsar el botón, se devuelva una página HTML con
una tabla que
contenga todos los elementos de la base de datos de productos que contienen la palabra introducida como
parte de su descripción. Si no hay ningún resultado en la búsqueda se deberá mostrar el correspondiente
mensaje. Ten en cuenta que el modo mayúscula/minúscula del texto introducido no tiene que coincidir
con la descripción del producto, es decir, si buscas "amd" y la descripión de un producto
contiene la palabra "AMD", dicho producto debe formar parte del resultado
de la búsqueda.
Puedes consultar el fichero Controles.hs
en los
ejemplos de la librería para ver como se usan los distintos tipos de controles que pueden aparecer en un formulario.
Para poder usar la función leerBDProd :: FilePath -> IO BDProd
dentro de un do
con tipo
Html
puedes usar la función unsafeIO :: IO a -> Html a
, de modo que
unsafeIO $ leerBDProd prodPath
tiene tipo Html BDProd
.
-
Mejora tu programa para que el formulario muestre además una lista
de selección (un elemento hSelectInput
) con todas las categorías
de la base de datos de categorías. Si se selecciona una categoría distinta
a "Todos", la búsqueda debe restringirse a los artículos que pertencen
a la categoría seleccionada. Las distintas opciones de la lista de categorías
deben obtenerse del fichero que almacena la base de datos de categorías, de modo que
no sea necesario modificar el programa si se cambia el contenido del fichero base de datos.
-
Mejora tu programa para que el formulario permita introducir más de una palabra en la
caja de texto. Además, deberá mostrarse una nueva lista de selección que permita que la
búsqueda devuelva solo los artículos que contienen todas las palabras introducidas en su
descripción o tan solo alguna de las palabras. Para ello, utiliza la siguiente definición de tipo
en tu programa:
data Condición = Alguna | Todas deriving (Eq, Show, Read, Enum)
instance CgiInput Condición
Puedes descomponer la cadena de entradas en las palabras que la forman usando la función predefinida
words
:: String -> [String]
. Se valorará positivamente el uso de una función de orden superior y las funciones predefinidas
all
y
any
para resolver
este apartado.
-
Mejora tu programa para que aparezca una nueva lista de selección en el formulario
que permita que la tabla con el resultado de la búsqueda esté ordenada por descripción o precio. Para ello, utiliza la siguiente definición de tipo
en tu programa:
data Orden = Descripción | Precio deriving (Eq, Show, Read, Enum)
instance CgiInput Orden
Se valorará positivamente el uso de la función de orden superior
sortBy
:: (a -> a -> Ordering) -> [a] -> [a]
de
la librería List
para resolver este apartado. Esta función toma como parámetro una función de comparación para los
elementos de
la lista que indica como ha de ordenarse. La función de comparación
compare
:: (Ord a) => a -> a -> Ordering
está
predefinida para los tipos simples.
-
Opcionalmente puedes añadir al programa alguna mejora que se te ocurra. Una interesante es devolver
la tabla de resultados poco a poco (en varias páginas), limitando el número de resultados de cada página tal como hacen los buscadores de Internet. Cada página
debería incluir un botón para ver los siguientes resultados de la búsqueda.
Otra ampliación sería
añadir al formulario un nuevo botón que nos lleve a otro formulario que proporcione una interfaz
de usuario para añadir o eliminar registros
de los ficheros de base de datos, es decir que permitan mantener las bases de datos en el servidor desde un cliente. Protege la entrada
en esta parte de la aplicación pidiendo una contraseña. Puedes usar el elemento hPasswordInput
(ver ejemplo
en Controles.hs
).
También es interesante que el buscador cree un fichero log en el que queden
almacenadas todas las consultas que se realizan (solo el texto a buscar y las condiciones, junto
con la fecha y hora de la consulta). Usa para ello la librería
Time
de Haskell y la función
unsafeIO
. Puedes incluso obtener la dirección IP del cliente que realiza la
consulta, para almacenarla en el log, unsando unsafeIO $ getEnv remoteHost
, donde
getEnv
es una función definida en la librería
System