Σημειωματάριο Πέμπτης 19 Νοεμβρίου 2015

Ένα πρόγραμμα για τηλεφωνικό κατάλογο

Σήμερα ξεκινήσαμε να φτιάχνουμε ένα πρόγραμμα τηλεφωνικού καταλόγου. Το πρόγραμμα αυτό θα είναι αρκετά μεγαλύτερο και πιο πολύπλοκο απ' ό,τι έχουμε δει ως τώρα. Θα έχει πολλές συναρτήσεις μέσα του (όλες οι λειτουργίες θα "πακετάρονται" ως συναρτήσεις, ώστε ο κώδικας να είναι πιο ευανάγνωστος και να τροποποιείται και ευκολότερα.

Το πρόγραμμα στη μορφή στην οποία το φέραμε σήμερα φαίνεται στο τέλος αυτής της σελίδας ολοκληρωμένο, και με αρκετά σχόλια και docstrings μέσα του ώστε να είναι κατανοητό.

Η "κυρίαρχη" συνάρτηση στο πρόγραμμα είναι η συνάρτηση menu() η οποία χειρίζεται το διάλογο με το χρήστη στο ανώτατο επίπεδο. Από τη menu() καλούνται συναρτήσεις οι οποίες υλοποιούν την κάθε λειτουργία του προγράμματος.

In [5]:
# -*- coding: utf-8 -*-
from IPython.display import clear_output # Χρησιμοποιούμε κάποια βιβλιοθήκη της ipython (που είναι η έκδοση της python που
                                         # χρησιμοποιώ στο μάθημα. Αν εσείς τρέξετε αυτό το πρόγραμμα σε κάποια διαφορετική
                                         # μορφή της python (π.χ. στο συνηθισμένο command line ή στο idle) τότε
                                         # θα πρέπει να αφαιρέσετε αυτή τη γραμμή. Ομοίως θα πρέπει να αφαιρέσετε τη γραμμή
                                         # clear_output() μέσα στη συνάρτηση menu() παρακάτω.
                                         # Αν θέλετε να τρέξετε αυτό το πρόγραμμα στο command line τότε αντικαταστήστε την γραμμή
                                         # αυτή με τη γραμμή
                                         #  import os
                                         # και μέσα στη συνάρτηση menu() τη γραμμή clear_output() με τη γραμμή
                                         # os.system('clear') (στο linux) ή os.system('cls') (windows).
                                         # Στο idle δε δουλεύει ο καθαρισμός οθόνης.


# Η παρακάτω μεταβλητή-λίστα mem είναι η κεντρική θέση μνήμης του προγράμματός μας, εκεί όπου αποθηκεύεται ο τηλεφωνικός
# κατάλογος. Κάθε θέση της mem είναι ένα λεξικό, κι έχουμε ένα μόνο λεξικό για κάθε άτομο. Στο λεξικό αυτό
# αποθηκεύονται διάφορες πληροφορίες για το άτομο αυτό. Κάνουμε τη σύμβαση ότι το μικρό όνομα και το επώνυμο κάθε
# ατόμου είναι μοναδικά. Δε μπορεί να υπάρχουν δηλ. δύο στοιχεία της λίστας mem με το ίδιο firstname και το ίδιο lastname.
#
# Δίνουμε εδώ αρχικές τιμές στη mem. Όταν αργότερα θα μάθουμε πώς διαβάζουμε και γράφουμε αρχεία θα δούμε πώς να γεμίζουμε
# (στην αρχή του προγράμματος) τη μεταβλητή mem από τα περιεχόμενα ενός αρχείου και πώς, στο τέλος του προγράμματος, να γράφουμε
# τα περιεχόμενα της mem στο ίδιο αρχείο, ώστε να τα βρούμε ξανά την επόμενη φορά που θα τρέξουμε το πρόγραμμα.
mem = [
       {"firstname": "Mihalis", "lastname": "Kolountzakis", "phone": "2810393834"},
       {"firstname": "Manolis", "lastname": "Manolakis", "phone": "2810281010"},
       {"firstname": "Yannis", "lastname": "Yannakis", "phone": "2810123123"},
       ]


def exists(fn, ln):
    """
    Ψάχνει τη λίστα mem για να δει αν υπάρχει άτομο με firstname ίσο με fn και lastname ίσο με ln.
    Αν υπάρχει μας επιστρέφει τη θέση του στη λίστα (>=0) αλλιώς επιστρέφει -1.
    Το αν ένα γράμμα είναι κεφαλαίο ή μικρό δεν επηρεάζει το αποτέλεσμα.
    """
    count = 0
    for person in mem:
        if ( fn.lower()==person["firstname"].lower() and 
             ln.lower() == person["lastname"].lower() ):
            return count # Βρέθηκε εγγραφή στο mem με αυτά τα στοιχεία
        else:
            count += 1
    return -1 # Δε βρέθηκε τέτοια εγγραφή στο mem


def readstring(old):
    """
    Διαβάζει ένα string από το χρήστη. Αν ο χρήστης δώσει enter επιστρέφει το string old,
    αλλιώς ό,τι έδωσε ο χρήστης. Έτσι είναι εύκολο για το χρήστη να αποδεχτεί την προτεινόμενη
    από το πρόγραμμα τιμή ή να επιλέξει να δώσει μια νέα τιμή.
    """
    s = raw_input("Give new value or enter for [%s]: " % old)
    if len(s) == 0:
        return old
    else:
        return s

# Η παρακάτω συνάρτηση είναι η συνάρτηση που λειτουργεί στο ανώτατο επίπεδο και καλεί όλες τις άλλες συναρτήσεις
def menu():
    """
    Ο χρήστης επιλέγει τι θα κάνει. Αν επιλέξει q επιστρέφει η συνάρτηση 1 αλλιώς
    επιστρέφει 0. Η συνάρτηση αυτή καλεί άλλες συναρτήσεις ανάλογα με τις επιθυμίες του
    χρήστη.
    """
    clear_output() # Καθαρίζει την οθόνη (μόνο για το σύστημα ipython)
    print """
    s. Look up a person
    i. Insert a person
    e. Edit a person
    d. Delete a person
    l. Show all
    w: Write to disk
    r: Read from disk
    q: Quit
    """
    ans = raw_input("Please choose: ") # Ο χρήστης επιλέγει τι θέλει να κάνει δίνοντας (συνήθως) ένα γράμμα

    if len(ans) != 1: # Πάτησε απλά enter. Επιστρέφουμε χωρίς να κάνουμε τίποτε.
        return 0

    if ans in "wr": # Οι επιλογές αυτές δεν έχουν υλοποιηθεί ακόμη
        print "Not implemented yet"
        return 0
    
    if ans=="s": # Αναζήτηση ατόμου
        lookup_and_print()
        return 0
    
    if ans=="i": # Εισαγωγή νέου ατόμου
        insert()
        return 0
    
    if ans=="e": # Επεξεργασία υπάρχοντος ατόμου
        edit()
        return 0
    
    if ans=="d": # Διαγραφή ατόμου
        delete()
        return 0
    
    if ans=="l": # Λίστα όλων των ατόμων του καταλόγου
        showall()
        return 0
    
    if ans=="q": # Έξοδος από το πρόγραμμα. Επιστρέφουμε 1 ώστε όποιος κάλεσε τη menu() να καταλάβει
                # ότι δεν πρέπει να την ξανακαλέσει.
        print "Bye"
        return 1
        
    return 0 # Αυτό εκτελείται ότι η επιλογή του χρήστη δεν είναι μια από τις παραπάνω


def showall():
    """
    Τυπώνει όλες τις εγγραφές του καταλόγου τη μια μετά την άλλη
    """
    count = 0
    print "------------------------------------"
    for person in mem:
        print "%2d: %s %s --> %s" % (count, person["firstname"],
                                        person["lastname"], person["phone"])
        count += 1
    print "------------------------------------"

    
def lookup_and_print():
    """
    Ρωτάει το χρήστη για ένα string. Διανύει το mem και τυπώνει όσα λεξικά (άτομα)
    έχουν το string ως κομμάτι του firstname  του lastname. Αγνοεί πεζά/κεφαλαία.
    """
    text = raw_input("What are you looking for: ") # Το κείμενο που αναζητάμε
    count = 0
    for person in mem:
        if ( text.lower() in person["firstname"].lower() or 
              text.lower() in person["lastname"].lower() ):
            print "%2d: %s %s --> %s" % (count, person["firstname"],
                                     person["lastname"], person["phone"])
            count += 1
            
def insert():
    """
    Εισάγει ένα νέο άτομο.
    """
    print "Insert new person"
    fn = raw_input("Give first name: ") # Διαβάζει από το χρήστη να στοιχεία του νέου ατόμου
    ln = raw_input("Give last name: ")
    pn = raw_input("Give phone number: ")
    if exists(fn, ln) >= 0: # Αν τέτοιο άτομο υπάρχει ήδη δεν το κρατάμε
        print "Person already in. Not added."
    else:
        mem.append( {"firstname": fn, "lastname": ln, "phone": pn} ) # Αλλιώς το προσθέτουμε στη μνήμη (λίστα mem)
    

def edit():
    """
    Αλλάζει τις τιμές σε ένα άτομο.
    """
    showall() # Πρώτα δείχνουμε όλα τα άτομα ώστε ο χρήστης να επιλέξει ποιο θέλει
              # να τροποποιήσει δίνοντας τον αριθμό του
    n = int(raw_input("Give the number of the person to edit: "))
    if n<0 or n>=len(mem): # Αριθμός εκτός ορίων
        print "Bad number"
        return    
    person = mem[n] 
    newfn = readstring(person["firstname"]) # Διαβάζουμε τις νέες τιμές του ατόμου
    newln = readstring(person["lastname"])  # δίνοντας ως προεπιλεγμένες τιμές τις ήδη υπάρχουσες
    newpn = readstring(person["phone"])
    if exists(newfn, newln) in [n, -1]: # Αν δεν υπάρχει αλλού το άτομο αυτό τότε κάνουμε την τροποποίηση
        person["firstname"] = newfn
        person["lastname"] = newln
        person["phone"] = newpn
    else:
        print "Already in. Doing nothing." # Το νέο όνομα που έδωσε ο χρήστης υπάρχει ήδη αλλού στο mem.
                                           # Δεν καταχωρείται η τροποποίηση.
    
    
    
    
def delete():
    """
    Ρωτάει το χρήστη για το ποιο άτομο να σβήσει (αριθμό) και το σβήνει.
    """
    showall() # Δίχνουμε όλα τα άτομα ώστε να επιλέξει ο χρήστης ποιο θα σβήσει με τον αριθμό του
    n = int(raw_input("Give the number of the person to delete: "))
    if n<0 or n>=len(mem): # Αριθμός εκτός ορίων
        print "Bad number"
        return
    del mem[n] # Το σβήνουμε από το mem

# Κυρίως πρόγραμμα. Καλούμε συνεχώς τη συνάρτηση menu() μέχρι αυτή να επιστρέψει 1, το οποίο συμβαίνει
# μόνο όταν ο χρήστης επέλεξε q (Quit).
while True:
    ret = menu()
    if ret == 1:
        break
    raw_input("Hit enter to continue: ") # Αν λείψει αυτή η εντολή από δω τότε η menu() ξανακαλείται άμεσα
                                         # που έχει ως συνέπεια ότι καθαρίζεται η οθόνη και δε βλέπει ο χρήστης
                                         # τα αποτελέσματα από την προηγούμενη λειτουργία.

    s. Look up a person
    i. Insert a person
    e. Edit a person
    d. Delete a person
    l. Show all
    w: Write to disk
    r: Read from disk
    q: Quit
    
Please choose: q
Bye

In []: