Hy, a better illustration. Made a simple phonebook

phonebook.jpg

1 Phonebook

Let's write a phone book application. To be clear this will just be a simple app, it should be used with a simple text ui, with a few flags. Then it should store the numbers and whatever other information in a seperate text file and search when you call it.

I will be writing this in hy, and I guess we will do this over several posts. Today I will setup stuff and do some initial coding. Actually since this will be a literate document the setup is already done, all the snippets will live in this file until I have a reasonable project. Then I might export it to use, or not depends.

1.1 Setup some simple functions

(defn process-new-input [nam &optional [phone 0] [email ""]]
  {nam {
        "phone" phone
        "email" email}})

Each name is a key to the addition information about said person, and the rest is optional and they are supplied with nil values. This way you only need supply the name for one of your contacts and the program should find the relevant information.

Another relevant function would be retrieval of information

(defn get-val [dic nam]
  (get dic nam)) 

Yeah okay that is almost not worth the effort to write, since it does nothing relevant yet. Though in case it is not a match I might want to be able to add some custom things there such as adding ask for information to add it to the database, or show similar names.

1.2 Flags to add or query the phonebook.

I think since I want to keep the ui minimal, that using flags for all of this is the simplest. I imagine that -a will be adding, not sure whether I prefer -q or -s for looking for information.

1.2.1 Adding

I imagine that the -a flag should have some flags used in conjunction, so the input is not entirely based on order, but each bit of information has an actual flag, or maybe even make them general, so you can also use the other flags in query, so in case you forget a name you can search or query by email instead. So it could be that you will write

phone.py -q -e "some@email.com"

and that will search for any field containing that email. This only gets terrible in performance when there is a lot of fields, but I think this is not a problem for most people. Since even with a few hundreds I think it should still be fast enough.

Anyway now for finding the right library for doing the flags, there is really a few options here. I would prefer to have something simple.

So I just go with the standard argparse, since I really don't need anything fancy for this minimal application.

(import argparse)

(defn add-field [name phone email]
(process-new-input (. args name) :phone (. args phone) :email (. args email)))
(setv parser (argparse.ArgumentParser))
(.add_argument parser "-a" "--add" :help "Add a new field to the database" :default False :nargs "?")
(.add_argument parser "-e" "--email" :help "Populate the email field")
(.add_argument parser "-n" "--name" :help "Populate the name field")
(.add_argument parser "-p" "--phone" :help "Populate the phone field")
(.add_argument parser "-q" "--query" :help "Query the database" :nargs "?" :dest "searching")
(setv args (.parse_args parser))
(comment (cond
[(= (. args add) None)
(setv name (. args name)
email (. args email)
phone (. args phone))
(add-field name phone email)]))

Using the previously defined function, though I still need it to persist One simple way to persist is to stuff the thing into a dictionary and then use pickle to marshall it to a file on disk. This is the simplest solution I can think of so that is what I think will be the best solution.

(import pickle)
(defn save-database [data]
  (.dump pickle data (open "phone.dat" "wb")))
(defn load-database []
  (setv data (.load (open "phone.dat", "rb"))))

However this does not solve my general problem of how to store the data in the program only that now I can take the object I will store it in and persist it to disk. A very dumb solution is to just use a global var for the storage, for now this should be sufficient, but I think it is a bad idea long term.

(setv *data* {})
(defn append-data [name email phone]
  (setv (get *data* name)
        {"email" email
         "phone" phone}))

This function would add the data to a global variable, which will then get persisted in a file and opened for search in case q is supplied by a flag. For now search should just be with name, later on I will need to workout how to make the search extend to other fields.

(defn search-database [n]
  (get *data* n))

That is very easy.

  1. Redefining process-new-line

    The version of process-new-line defined at the top is not really suitable for my needs, therefore I will need to redefine it, in such a manner that it will append to a global variable. Luckily this is fairly easy.

    (setv *data* {})
    (defn process-new-line [nam &optional [phone 0] [email ""]]
      (setv (get *data* nam) {"phone" phone
                              "email" email}))
    

    This would allow me to have build a database over time. Now I just need to put the pieces together to see what I how so far.

    (import pickle)
    (defn save-database [data]
      (.dump pickle data (open "phone.dat" "wb")))
    (defn load-database []
      (setv data (.load (open "phone.dat", "rb"))))
    (setv *data* {})
    (defn append-data [name email phone]
      (setv (get *data* name)
            {"email" email
             "phone" phone}))
    

    (defn search-database [n]
    (get data n))
    (setv data {})
    (defn process-new-line [nam &optional [phone 0] [email ""]]
    (setv (get data nam) {"phone" phone
    "email" email}))

    (import argparse)

    (defn add-field [name phone email]
    (process-new-input (. args name) :phone (. args phone) :email (. args email)))
    (setv parser (argparse.ArgumentParser))
    (.add_argument parser "-a" "--add" :help "Add a new field to the database" :default False :nargs "?")
    (.add_argument parser "-e" "--email" :help "Populate the email field")
    (.add_argument parser "-n" "--name" :help "Populate the name field")
    (.add_argument parser "-p" "--phone" :help "Populate the phone field")
    (.add_argument parser "-q" "--query" :help "Query the database" :nargs "?" :dest "searching")
    (setv args (.parse_args parser))
    (comment (cond
    [(= (. args add) None)
    (setv name (. args name)
    email (. args email)
    phone (. args phone))
    (add-field name phone email)]))
    (load-database)
    (cond [(= (. args add) None)
    (setv email (. args email)
    phone (. args phone)
    name (. args name)
    (process-new-line name :phone phone :email email))]
    [(= (. args query) None)
    (setv name (. args name)
    (search-database name))])
    (save-database data)

    That should be at least viable as a first version of this.

H2
H3
H4
3 columns
2 columns
1 column
1 Comment
Ecency