Ekstraopgaver til Løbeseddel 6: Flere Web-services

for Databasestøttet Webpublicering

af Martin Elsman og Niels Hallenberg sidst rettet 15. februar, 2002


Dette ekstrasæt til løbeseddel 6 indeholder tre opgaver der øver programmering med Tcl, regulære udtryk, løkker og overførsel af form-variable.

Check af cpr-nummer

I denne opgave skal du anvende et regulært udtryk til at checke om et indtastet cpr-nummer opfylder modulus 11 kravet, dvs. er et gyldigt dansk cpr-nummer.

At et cpr-nummer opfylder modulus 11 kravet betyder ikke nødvendigvis at der findes en person med dette nummer. Dog vil alle cpr-numre opfylde modulus 11 kravet, så det er en god test der sikrer mod taste fejl.

Du skal lave en fil cpr.html som indeholder et inddatafelt hvor brugeren kan indtaste et cpr-nummer, samt en "submit"-knap der kalder Tcl programmet cpr.tcl:

Eksempel på cpr.html

I Tcl programmet cpr.tcl laver du et regulært udtryk som ved hjælp af kommandoen regexp binder de 10 tal i cpr-nummeret til variable c1,...,c10; dvs. hvert tal i cpr-nummeret bindes til hver sin variabel.

Eksempel: Med cpr-nummer 291270-1234 bindes c1 til 2, c2 til 9, c3 til 1, ..., c10 til 4.

Hvis det indtastede cpr-nummer ikke opfylder dit regulære udtryk, så returnerer du en passende fejlmeddelelse.

Du skal lave en procedure chk_modulus11, som tager de 10 tal som argument og udfører et modulus 11 check på de 10 tal:

proc chk_modulus11 {c1 c2 c3 c4 c5 c6 c7 c8 c9 c10} {
  set sum [expr ...]
  if {[expr ...] == 0} {
    return 1
  } else {
    return 0
  }
}
For at udføre et modulus 11 check sætter du variablen sum lig følgende beregning:
  c1*4 + c2*3 + c3*2 + c4*7 + c5*6 + c6*5 + c7*4 + c8*3 + c9*2 + c10*1
hvor c1, ..., c10 er de ti tal i cpr-nummeret. Hvis indholdet af variablen sum modulus 11 giver 0, så er modulus 11 checket opfyldt, dvs. du skal udføre beregningen sum % 11 og se om resultatet giver 0.

Følgende billeder viser et cpr-nummer som opfylder modulus 11 kontrollen og et cpr-nummer som ikke opfylder modulus 11 kontrollen:

Cpr-nummer som opfylder modulus 11 kontrol

Cpr-nummer som ikke opfylder modulus 11 kontrol

Bemærk, at modulus 11 kontrollen ikke checker om fødselsdatoen er valid, så derfor kan du forbedre servicen ved også at kontrolere, at fødselsdatoen er en korrekt dato. Du kan f.eks. anvende procedureren date_ok fra weekday.tcllb5-ekstra.html til at kontrollere at datoen findes.

Portoberegner med en valgmenu

I denne service angiver brugeren et brevs vægt i gram, hvorefter programmet beregner portoen for at brevet kan sendes indenlands i Danmark. Tabellen nedenfor viser taksterne for almindelige breve:

Vægt i gramTakst i kr.
mindre end 0Brev kan ikke sendes
op til 204,00
op til 505,25
op til 1005,75
op til 2509,75
op til 50017,00
op til 100021,00
op til 150028,00
op til 200030,00
mere end 2000Brev kan ikke sendes

Brugeren vælger vægten fra en valgliste (select). Når brugeren har valgt en vægt klikker brugeren på knappen mæket Beregn, hvorefter Tcl programmet returnererer en HTML side med portoen angivet. Den første figur nedenfor viser portoberegneren første gang brugeren ser den, og det andet billede viser den HTML side brugeren får retur. Læg mærke til, at brugeren kan vælge en ny vægt i den HTML side der også indeholder portoen på det sidste valg.

Portoberegner før valg

Portoberegner efter valg

Det er en fordel at registrere vægte og portoer i Tcl programmet som en liste af lister:

set vaegte [list [list "mindre end 0" "kan ikke sendes"] [
                  list "op til 20" "kræver 4.00 i porto"] [...

Ideen er, at hver vaegt/porto par består af to tekster: vægt og porto. Parret med vægt og porto for 'mindre end 0' har indeks 0 i listen vaegte og parret for 'op til 20' har indeks 1 i listen. Koden for valglisten i formen, kan f.eks. se således ud:

<select name=vaegt>
<option value="0">mindre end 0 gram
<option value="1">op til 20 gram
...
</select>

Hvis brugeren vælger 'mindre end 0 gram', så registreres indeks 0 i form-variablen vaegt, og hvis 'op til 20 gram' vælges registreres indeks 1 i form-variablen vaegt. På den måde kan vi i Tcl programmet finde frem til både vaegt og porto på det brev som brugeren skal sende.

Hvis variablen vaegt indeholder indekset på det brev brugeren har valgt (enten 0 eller 1 i eksemplet ovenfor), så kan vi i Tcl programmet få

Opgaven består kun af en fil porto.tcl, som undersøger om form-variablen vaegt er tilstede. Hvis form-variablen ikke er tilstede, så returneres en tom portoberegner til brugeren (første billede ovenfor); ellers returneres portoen på det valgte brev samt en ny form (andet billede ovenfor).

Du kan bygge dit program omkring følgende skabelon:

set_form_variables 0

proc home_page { title body } { ... }

proc my_return_page { title body } {
  ns_return 200 text/html [home_page $title $body]
}

set vaegte [list [list "mindre end 0" "kan ikke sendes"] [
                  list "op til 20" "kræver 4.00 i porto"] [...

proc mk_vaegt_liste {vaegte} { 
  # Returner en valgliste (select)
}

proc porto_form {vaegte} {
  set page "<h2>Portoberegner</h2>
  <form method=post action=porto.tcl>
  ...
  </form>"
}

if {![info exists vaegt]} {
  # Returner en HTML side, hvor bruger kan vælge en vægt.
} else {
  # Find porto givet vaegt.

  # Find teksten på vaegt

  # Returner en HTML side med angivelse af tekst på vægt og porto, 
  # samt en ny form, hvor bruger kan vælge en ny vægt.
}
Ekstra: Udvid med at anvende et regulært udtryk til at checke, at form-variablen vaegt indeholder et heltal.

Portoberegner med økonomibreve

Dene opgave er en udvidelse af opgaven ovenfor. Du skal tilføje en enkelt checkboks (input boks med type checkboks). Checkboksen angiver om brevet skal sendes som økonomibrev. Hvis checkboksen er valgt sendes brevet som et økonomibrev. Tabellen nedenfor viser taksterne for økonomibreve:

Vægt i gramTakst i kr.
mindre end 0Brev kan ikke sendes
op til 203,75
op til 505,00
op til 1005,50
op til 2509,25
op til 50016,00
op til 100020,00
op til 150027,00
op til 200029,00
mere end 2000Brev kan ikke sendes

En måde at udvide programmet på er at udvide listen vaegte, således at der til hver vægt findes to portoer, en for almindelig forsendelse og en for økonomibreve:

set vaegte [list [list "mindre end 0" "kan ikke sendes" "kan ikke sendes"] [
                  list "op til 20" "kræver 4.00 i porto" "kræver 3.75 i porto som økonomibrev"] [
                  ...]]
Når portoen beregnes henter du portoen fra indeks 1 hvis checkboksen ikke er valgt og portoen fra indeks 2 hvis checkboksen er valgt.

Portoberegner med økonomibreve før valg

Portoberegner med økonomibreve efter valg

Kassestrimmel

I denne opgave skal du simulere et simpelt kasseapparat. Opgaven øver bl.a. lister, løkker, skjulte formvariable og valglister.

Eksempel på en kassestrimmel

Opgaven synes måske noget svær, men hvis du går systematisk frem, og gennemtænker opbygningen af programmet inden du koder, så er det ikke så svært. Den vejledende løsning er på 61 linier. alt inklusive.

Brugeren præsenteres en form der indeholder

Formen indeholder følgende form-variable:

Opgaven består kun af en fil kassestrimmel.tcl, som undersøger om de fire form-variable er tilstede. Hvis form-variablene ikke er tilstede, så returneres en tom kassestrimmel til brugeren:

Eksempel på en tom kassestrimmel

Hvis form-variablene er tilstede, så opdateres kassestrimlen og den løbende sum, hvorefter den opdaterede kassestrimmel returneres til brugeren, således at brugeren kan gøre flere køb. Figuren viser ændringen fra det første billede ovenfor, og efter at brugeren har valgt to liter saftevand.

Eksempel på en kassestrimmel med et nyt koeb

Du kan bygge dit program omkring følgende skabelon:

set_form_variables 0

proc home_page { title body } {...}

proc my_return_page { title body } {
  ns_return 200 text/html [home_page $title $body]
}

set varer [list [list "1½ Coca Cola" 16.50] [list "1½ kg. kartofter" 10.25] ...]

proc mk_vare_liste {varer} {

  # Her kan du bygge valglisten, dvs. HTML kode (select)
  # som skal indsættes i den form brugeren ser.
 
}

# strimmel_form genererer HTML kode for den form brugeren
# ser. Procedureren kaldes både, når strimlen er tom, og når den ikke er
# tom.
proc strimmel_form {varer sum strimmel} {
  # variabel varer indeholder listen af varer

  # variabel sum indeholder den løbende sum, som skal skal udskrives i
  # formen og som er skjult i formen.
 
  # variabel strimmel indeholder strimlen, som skal udskrives i
  # formen, og som er skjult i formen.

  set page "<h2>Kassestrimmel</h2>
  <form method=post action=kassestrimmel.tcl>
  ...
  </form>"
}

if {![info exists sum] ||
    ... } {

  # Her returnerer du en tom kassestrimmel ved bl.a. at kalde
  # procedureren strimmel_form med varesortimentet, sum lig 0 og
  # strimmel lig den tomme streng.
} else {
  # Beregn pris på den valgte vare

  # Beregn varenavn på den valgte vare

  # Udvid strimmel med det nye køb

  # Beregn ny løbende sum

  # Returerer en opdateret kassestrimmel ved bl.a. at kalde
  # procedureren strimmel_form med varesortimentet, 
  # den opdaterede sum og den opdaterede strimmel.
}
Ekstra: Udvid med at anvende regulære udtryk til at checke, at form-variablene indeholder værdier der syntaktisk svarer til det forvende.
mael@it.edu, nh@it.edu