Author Topic: Menu me this....  (Read 4480 times)

Offline AIR

  • BASIC Developer
  • Posts: 684
Menu me this....
« on: October 22, 2018, 12:07:41 PM »
Okay, so here's a coding challenge that can be used in the real world.  I had to do this for multiple OS's for work.

It's a terminal-based front-end to back up user data (I'm excluding the backup part).

The idea is to present the user/technician with a menu were they can select a user to backup.

Requirements:

  • A menu with a title
  • The ability to pass both the Menu Title and the Home Folder location where the user data is stored (under *nix this is typically "/home", on macOS/Windows it's "/Users")
  • The ability to specify exclusions so that certain accounts/folders are NOT displayed
  • The menu needs to list a number associated with the user id, and the user logon id itself
  • A prompt that only accepts numerical input, within the range specified in the menu
  • A warning message if the input is not in range
  • Upon a successful selection, output the user id that was selected

Here is an implementation in Bash (more to come):

Code: Bash
  1. #!/bin/bash
  2.  
  3. EXCLUDE='Shared|Guest'
  4.  
  5. function SelectFolder() {
  6.     # PARAMETERS:
  7.     #    $1 - Menu Title
  8.     #    $2 - User Folders Location
  9.  
  10.     clear
  11.     TITLE="$1"
  12.     TLEN=${#TITLE}
  13.  
  14.     echo "$TITLE"
  15.     printf %${TLEN}s | tr " " "="
  16.     echo
  17.  
  18.     USER_LOC=$(ls "$2" | egrep -v $EXCLUDE)
  19.  
  20.     PS3="Select User: "
  21.  
  22.     select USER_ACCOUNT in $USER_LOC; do
  23.         test -n "$USER_ACCOUNT" && break
  24.         echo ">>> Invalid Selection <<<"
  25.     done
  26. }
  27.  
  28. # change the second parameter depending on OS...
  29. SelectFolder "Armando's Test Menu" "/Users"
  30.  
  31. printf "\nYou Selected: $USER_ACCOUNT\n"
  32.  


AIR.
« Last Edit: October 22, 2018, 12:10:39 PM by AIR »

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Menu me this....
« Reply #1 on: October 22, 2018, 12:14:08 PM »
PYTHON VERSION

(It's an OPEN FORUM after all!!!  8))

Code: Python
  1. #!/usr/bin/env python
  2.  
  3. import glob,os,sys,time
  4.  
  5. excluded = '''Shared
  6.              Guest'''.split()
  7.                          
  8. def clear():
  9.         sys.stderr.write("\x1b[2J\x1b[H")
  10.  
  11. def SelectFolder(menu_title,path):
  12.  
  13.         loop = True
  14.         selection = {}
  15.         x = [x for x in glob.glob(path+'/*') if os.path.isdir(x) and not (os.path.basename(x) in excluded)]
  16.         while loop:
  17.                 clear()
  18.                 print menu_title.center(30,"-")
  19.  
  20.                 for cnt,item in enumerate(x):
  21.                         print '{}) {}'.format(cnt+1, os.path.basename(item))
  22.                         selection[str(cnt+1)] = item
  23.  
  24.                 print "-"*30
  25.                 print
  26.                 choice = raw_input('Select a User Folder: ')
  27.  
  28.                 try:
  29.                         selected = selection[choice]
  30.                         loop = False
  31.                 except KeyError:
  32.                         print ">> Invalid Selection <<"
  33.                         time.sleep(1)
  34.                         continue
  35.  
  36.         return selected
  37.  
  38. # change second parameter according to OS...
  39. selected = SelectFolder(" Armando's Test Menu ",'/Users')
  40. print "\nYou Selected: %s" % os.path.basename(selected)
  41.  

AIR.

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Menu me this....
« Reply #2 on: October 22, 2018, 12:18:08 PM »
And for all the BASIC enthusiasts, a BACON VERSION  ;D

Code: Text
  1. DECLARE Exclude$ = "Guest|Shared"
  2.  
  3. FUNCTION SelectFolder$(Title$, Folder$)
  4.     LOCAL Menu$ ASSOC STRING
  5.     LOCAL tLen,cnt,Amount TYPE int
  6.     LOCAL Border$ TYPE STRING
  7.     LOCAL Loop TYPE bool
  8.  
  9.     tLen = LEN(Title$)
  10.     Loop = TRUE
  11.     Border$ = FILL$(LEN(Title$), 45 )
  12.  
  13.     WHILE Loop
  14.         Count = 1
  15.         OPEN Folder$ FOR DIRECTORY AS mydir
  16.  
  17.         CLEAR
  18.  
  19.         PRINT Title$
  20.  
  21.         PRINT Border$
  22.  
  23.         REPEAT
  24.             GETFILE myfile$ FROM mydir
  25.             IF LEFT$(myfile$,1)== "." OR ISFALSE(LEN(myfile$)) OR REGEX(myfile$,Exclude$) THEN
  26.                 CONTINUE
  27.             ELSE
  28.                 Menu$(STR$(Count)) = myfile$
  29.                 PRINT Count,") ", myfile$
  30.                 INCR Count
  31.             ENDIF
  32.         UNTIL ISFALSE(LEN(myfile$))
  33.        
  34.         CLOSE DIRECTORY mydir
  35.  
  36.         PRINT Border$
  37.        
  38.         Amount = NRKEYS(Menu$)
  39.         PRINT
  40.  
  41.         INPUT "Select User: ",Selection$
  42.         blah = VAL(Selection$)
  43.         IF (blah < 1 OR blah > Amount) THEN
  44.             PRINT ">>> Invalid Selection <<"
  45.             SLEEP 1000
  46.             CONTINUE
  47.         ELSE
  48.             Loop = FALSE
  49.         ENDIF
  50.     WEND
  51.     RETURN Menu$(Selection$)
  52. END FUNCTION
  53.  
  54. selected$ = SelectFolder$("Armando's Test Menu","/Users")
  55.  
  56. PRINT
  57. PRINT "You Selected: ", selected$
  58.  

AIR.

Offline John

  • Forum Support / SB Dev
  • Posts: 2741
    • ScriptBasic Open Source Project
Re: Menu me this....
« Reply #3 on: October 22, 2018, 12:43:23 PM »
Would it be okay if I submitted my Script BASIC cross platform command line backup select utility to accept a path/file mask as an argument so the utility could be used in a 'batch' file? STDIO

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Menu me this....
« Reply #4 on: October 22, 2018, 12:45:00 PM »
Sure, as long as the core requirements are met.

Offline John

  • Forum Support / SB Dev
  • Posts: 2741
    • ScriptBasic Open Source Project
Re: Menu me this....
« Reply #5 on: October 22, 2018, 12:57:39 PM »
I should have read your requirements better.  >:(

I thought you were looking for a frontend file select with a default prefix directory path.

I'll give your requirements a try.


Offline John

  • Forum Support / SB Dev
  • Posts: 2741
    • ScriptBasic Open Source Project
Re: Menu me this....
« Reply #6 on: October 22, 2018, 02:20:18 PM »
I gave your BaCon example a try but had to make the _Bool change and change /Users to /home.

Code: [Select]
    DECLARE Exclude$ = "Guest|Shared"

    FUNCTION SelectFolder$(Title$, Folder$)
        LOCAL Menu$ ASSOC STRING
        LOCAL tLen,cnt,Amount TYPE int
        LOCAL Border$ TYPE STRING
        LOCAL Loop TYPE _Bool

        tLen = LEN(Title$)
        Loop = TRUE
        Border$ = FILL$(LEN(Title$), 45 )

        WHILE Loop
            Count = 1
            OPEN Folder$ FOR DIRECTORY AS mydir

            CLEAR

            PRINT Title$

            PRINT Border$

            REPEAT
                GETFILE myfile$ FROM mydir
                IF LEFT$(myfile$,1)== "." OR ISFALSE(LEN(myfile$)) OR REGEX(myfile$,Exclude$) THEN
                    CONTINUE
                ELSE
                    Menu$(STR$(Count)) = myfile$
                    PRINT Count,") ", myfile$
                    INCR Count
                ENDIF
            UNTIL ISFALSE(LEN(myfile$))

            CLOSE DIRECTORY mydir

            PRINT Border$

            Amount = NRKEYS(Menu$)
            PRINT

            INPUT "Select User: ",Selection$
            blah = VAL(Selection$)
            IF (blah < 1 OR blah > Amount) THEN
                PRINT ">>> Invalid Selection <<"
                SLEEP 1000
                CONTINUE
            ELSE
                Loop = FALSE
            ENDIF
        WEND
        RETURN Menu$(Selection$)
    END FUNCTION

    selected$ = SelectFolder$("Armando's Test Menu","/home")

    PRINT
    PRINT "You Selected: ", selected$


jrs@jrs-laptop:~/BaCon/bacon-3.7.3$ ./air

Armando's Test Menu
-------------------
1) jrs
-------------------

Select User: 1

You Selected: jrs
jrs@jrs-laptop:~/BaCon/bacon-3.7.3$


This is a much easier task than I first imagined.

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Menu me this....
« Reply #7 on: October 22, 2018, 02:27:53 PM »
I gave your BaCon example a try but had to make the _Bool change and change /Users to /home.

Hmm.  It worked with bool here on macOS and Raspbian, are you using the latest Bacon?

Quote from: John
This is a much easier task than I first imagined.

Yeah, it's all about putting the pieces together.  Once you get there, it's pretty easy when you look back...

AIR.

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Menu me this....
« Reply #8 on: October 22, 2018, 02:30:11 PM »
NIM VERSION

Code: Text
  1. import os,ospaths,strutils,tables
  2.  
  3. proc input(prompt: string): int =
  4.     var
  5.         selection:string
  6.  
  7.     stdout.write prompt
  8.     selection = readLine(stdin)
  9.     return selection.parseInt
  10.  
  11.  
  12. proc SelectFolder(menu_title:string, path:string): string =
  13.     var
  14.         Count,Choice:int
  15.         Loop = true
  16.         userFolder,selected, Border:string
  17.         Exclude = ["Shared","Guest"]
  18.         menu = initTable[int, string]()
  19.    
  20.     Border = "-".repeat(menu_title.len)
  21.  
  22.     while Loop:
  23.         stdout.write "\x1b[2J\x1b[H"
  24.         echo menu_title
  25.         echo Border
  26.         Count = 1
  27.         for kind, path in walkDir(path):
  28.             userFolder = path.extractFilename
  29.             if userFolder.startsWith(".") or Exclude.contains(userFolder):
  30.                 continue
  31.             else:
  32.                 menu[Count] = userFolder
  33.                 echo Count,") ", userFolder
  34.                 Count += 1
  35.        
  36.         echo Border
  37.  
  38.         try:
  39.             Choice = input("Select a User Folder: ")
  40.             selected = menu[Choice]
  41.             Loop = false
  42.         except KeyError, ValueError:
  43.             echo ">> Invalid Selection <<"
  44.             sleep 1000
  45.             continue
  46.        
  47.     return selected
  48.  
  49. var selected = SelectFolder("Armando's Test Menu","/Users")
  50.  
  51. echo "\nSelected User: ",selected

AIR.

Offline John

  • Forum Support / SB Dev
  • Posts: 2741
    • ScriptBasic Open Source Project
Re: Menu me this....
« Reply #9 on: October 22, 2018, 02:42:24 PM »
On Linux and I assume iOS one must run this as root.

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Menu me this....
« Reply #10 on: October 22, 2018, 02:43:55 PM »
On Linux and I assume iOS one must run this as root.

Not the menu.  If you go ahead and add code to do the actual backup, then yes.  But that's outside of the scope of this "challenge"...

BTW, root is not usually in the /home folder.  It's typically at /root in Linux, and in /var under macOS (not iOS).
« Last Edit: October 22, 2018, 03:04:10 PM by AIR »

Offline John

  • Forum Support / SB Dev
  • Posts: 2741
    • ScriptBasic Open Source Project
Re: Menu me this....
« Reply #11 on: October 22, 2018, 04:12:52 PM »
Quote from: AIR
BTW, root is not usually in the /home folder.  It's typically at /root in Linux, and in /var under macOS (not iOS).

Bad assumption on my part without checking first.

Here is the Script BASIC submission.

Code: Script BASIC
  1. fn = FREEFILE()
  2. Exclude = "Guest|Shared"
  3. Title = "Armando's Test Menu"
  4. file_list = ""
  5. Border = STRING(LEN(Title), "-")
  6. Search_Options = SbCollectDirectories
  7. OPEN DIRECTORY "/home" PATTERN "" OPTION Search_Options AS #fn
  8. RESET DIRECTORY #fn
  9. fName = NEXTFILE(fn)
  10. WHILE fName <> undef
  11.   IF NOT INSTR(Exclude, fName) THEN file_list &= fName & "\n"
  12.   file_list &= fName & "\n"
  13.   fName = NEXTFILE(fn)
  14. WEND
  15. CLOSE DIRECTORY #fn
  16. SPLITA file_list BY "\n" TO users
  17. PRINT Title,"\n",Border,"\n"
  18. FOR idx = 0 TO UBOUND(users)
  19.   PRINT idx,") ",users[idx],"\n"
  20. NEXT
  21. PRINT Border,"\n","Select User: "
  22. LINE INPUT uid
  23. uid = VAL(CHOMP(uid))
  24. PRINT "You Selected: ", users[uid], "\n"
  25.  


jrs@jrs-laptop:~/sb/examples/test$ scriba air.sb
Armando's Test Menu
-------------------
0) jrs
-------------------
Select User: 0
You Selected: jrs
jrs@jrs-laptop:~/sb/examples/test$


« Last Edit: October 22, 2018, 04:16:10 PM by John »

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Menu me this....
« Reply #12 on: October 22, 2018, 04:28:06 PM »
Code: [Select]
Armando's Test Menu
-------------------
[0] .localized
[1] Shared
[2] Shared
[3] Guest
[4] Guest
[5] riveraa
-------------------
Select User: 9
You Selected: undef

It's not filtering plus I'm seeing dupes on my Mac.  Also missing the Warning message if the selection is out of range.  And requirement #2 isn't exactly met.\

Dupes are due to this:
Code: Script BASIC
  1. WHILE fName <> undef
  2.   IF NOT INSTR(Exclude, fName) THEN file_list &= fName & "\n"
  3.   file_list &= fName & "\n"
  4.   fName = NEXTFILE(fn)
  5. WEND

You added to file_list twice.  LOL.  I hate when that happens!!

AIR.
« Last Edit: October 22, 2018, 04:32:08 PM by AIR »

Offline John

  • Forum Support / SB Dev
  • Posts: 2741
    • ScriptBasic Open Source Project
Re: Menu me this....
« Reply #13 on: October 22, 2018, 04:52:45 PM »
It was going to be a IF / END IF but decided on one line and forgot to delete it.   ;D

Try this.

Code: Script BASIC
  1. ' AIR - Select user for backup
  2.  
  3. FUNCTION SelectFolder(Title, Folder)
  4.   LOCAL fn, Exclude, Border, file_list, fName, users, idx, uid
  5.   fn = FREEFILE()
  6.   Exclude = "Guest|Shared"
  7.   Border = STRING(LEN(Title), "-")
  8.   file_list = ""
  9.   OPEN DIRECTORY Folder PATTERN "" OPTION SbCollectDirectories AS #fn
  10.   RESET DIRECTORY #fn
  11.   fName = NEXTFILE(fn)
  12.   WHILE fName <> undef
  13.     IF NOT INSTR(Exclude, fName) = undef THEN file_list &= fName & "\n"
  14.     fName = NEXTFILE(fn)
  15.   WEND
  16.   CLOSE DIRECTORY #fn
  17.   SPLITA file_list BY "\n" TO users
  18.   PRINT Title,"\n",Border,"\n"
  19.   FOR idx = 0 TO UBOUND(users)
  20.     PRINT idx,") ",users[idx],"\n"
  21.   NEXT
  22.   Enter:
  23.   PRINT Border,"\n","Select User: "
  24.   LINE INPUT uid
  25.   uid = VAL(CHOMP(uid))
  26.   IF uid > UBOUND(users) THEN
  27.     PRINT ">> Invalid Selection <<", "\n"
  28.     GOTO Enter
  29.   END IF
  30.   SelectFolder = users[uid]
  31. END FUNCTION
  32. selected = SelectFolder("Armando's Test Menu", "/home")
  33. PRINT "You Selected: ", selected, "\n"
  34.  


jrs@jrs-laptop:~/sb/examples/test$ scriba air.sb
Armando's Test Menu
-------------------
0) jrs
-------------------
Select User: 1
>> Invalid Selection <<
-------------------
Select User: 0
You Selected: jrs
jrs@jrs-laptop:~/sb/examples/test$ ls -l /home
total 12
drwxr-xr-x   2 root root 4096 Oct 22 16:48 Guest
drwxr-xr-x 133 jrs  jrs  4096 Oct 22 14:01 jrs
drwxr-xr-x   2 root root 4096 Oct 22 16:48 Shared
jrs@jrs-laptop:~/sb/examples/test$



« Last Edit: October 22, 2018, 05:48:48 PM by John »

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Menu me this....
« Reply #14 on: October 22, 2018, 06:04:05 PM »
Code: [Select]
Armando's Test Menu
-------------------
0) .localized
1) riveraa
-------------------
Select User:

That's what I get with your code on my Mac.

EDIT:  You also don't account for non-numeric input, so if I input a letter or a string of letters, the result is the first entry in the AssocArray.

I get the following with SB code that I wrote (I modified what you originally posted):

Code: [Select]
Armando's Test Menu
-------------------
1) riveraa
-------------------
Select User:

Here's my take on it, this version refreshes the screen after showing the warning message:

Code: Script BASIC
  1. FUNCTION  SelectFolder(Title,Path)
  2.   fn = FREEFILE()
  3.   Exclude = "Guest|Shared|Public"
  4.   file_list = ""
  5.   myloop = 1
  6.   Border = STRING(LEN(Title), "-")
  7.   Search_Options = SbCollectDirectories
  8.  
  9.  
  10.   WHILE myloop
  11.     OPEN DIRECTORY Path PATTERN "" OPTION Search_Options AS #fn
  12.     RESET DIRECTORY #fn
  13.     fName = NEXTFILE(fn)
  14.  
  15.     WHILE fName <> undef
  16.       IF NOT INSTR(Exclude, fName) = undef THEN
  17.         IF LEFT(fName,1) <> "." THEN file_list &= fName & "\n"  
  18.       END IF
  19.       fName = NEXTFILE(fn)
  20.     WEND
  21.  
  22.     CLOSE DIRECTORY #fn
  23.  
  24.     SPLITA file_list BY "\n" TO users
  25.  
  26.     PRINT "\x1b[2J\x1b[H"
  27.  
  28.     PRINT Title,"\n",Border,"\n"
  29.  
  30.     FOR idx = 0 TO UBOUND(users)
  31.         PRINT idx+1,") ",users[idx],"\n"
  32.     NEXT
  33.  
  34.     PRINT Border,"\n","Select User: "
  35.  
  36.     LINE INPUT uid
  37.  
  38.     uid = VAL(CHOMP(uid-1))
  39.  
  40.     IF users[uid] = undef THEN
  41.       PRINT ">> Invalid Selection <<\n"
  42.       SLEEP 1
  43.       file_list = undef
  44.     ELSE
  45.       myloop = 0
  46.     END IF
  47.   WEND
  48.  
  49.   SelectFolder = users[uid]
  50. END FUNCTION
  51.  
  52. selected = SelectFolder("Armando's Test Menu", "/Users")
  53.  
  54. PRINT "\nYou Selected: ",selected,"\n"

AIR.
« Last Edit: October 22, 2018, 06:07:33 PM by AIR »