Eksamen, efterår 2000

for Database-baseret Web-publicering, efterår 2000

af Martin Elsman


Introduktion

I dette eksamenssæt skal du implementere dele af et webbaseret restaurantbestillingssystem. Ideen med systemet er at restaurantkunder kan bestille pizza og andre retter over internettet, hvorefter bestillingerne kan ses af medarbejderne på den pågældende restaurant. For at gøre det nemmere for kunderne at vælge mellem retterne, ønskes der også implementeret et bestillingsbarometer til sammenligning af de enkelte retters popularitet.

Opgave 1 (20 procent) - HTML

Nedenfor vises en kundehovedside for det web-baserede bestillingssystem:

Kundehovedsiden vises ovenfor for en restaurant i nabolaget og giver mulighed for at kunder kan afsende en bestilling ved at vælge den ønskede ret i SELECT-boksen, indtaste telefonnummer i tekstboksen og derefter trykke på knappen ``Afsend Bestilling''.

Opgave 1.1

Opskriv HTML koden for formen i siden ovenfor. Besvarelsen skal udelukkende indeholde HTML-koden for formen og ikke HTML-koden for hele siden:
  <form action=order.tcl>
    <input type=hidden name=store_id value=1>
    ...
  </form>
Udover den skjulte formvariabel store_id, som angiver identiteten på restauranten, skal formen indeholde tre form-elementer:
  1. En SELECT-boks med attribut name=dish_id. Til højre vises formen med SELECT-boksen udfoldet; værdierne for hver af mulighederne i SELECT-boksen ønskes sat til henholdsvis 1, 2 og 3.
  2. En tekstboks med attributter name=phone og size=8.
  3. En submit-knap med attribut value="Afsend Bestilling".

Opgave 1.2

I denne opgave skal du opskrive HTML-koden der omgiver formen på bestillingssiden ovenfor. Der er følgende krav til siden: Bemærk: Rammen rundt om billedet for siden er ikke en del af HTML-koden som du skal skrive. For at spare på skrivningen i opgave 1.2 kan du blot skrive *FORM* for at henvise til besvarelsen af opgave 1.1.

Opgave 2 (20 procent) - Tcl

Opgave 2.1

Vis HTML-koden som er resultatet af at kalde tcl-proceduren star_graph, vist nedenfor, med listen af bestillingsforekomster
  [list [list "Pizza nr. 9" 5] [list "Pizza nr. 5" 2]]
givet som argument:
  proc star_graph { l } {
      set res "<table width=100%>"
      foreach e $l {
	  set name [lindex $e 0]
	  set count [lindex $e 1]
	  set n $count
	  set bar ""
	  while { $n > 0 } {
	      append bar "*"
	      incr n -1
	  }
	  append res "<tr><td width=50%>$name</td>
                          <td width=50%>$count: $bar</td>
                      </tr>"
      }
      return "$res</table>"
  }

Opgave 2.2

Skriv en tcl-procedure max_count, som tager en liste af bestillingsforekomster som argument og returnerer det maksimale antal gange en ret er bestilt.

Hint: Ved følgende kald af proceduren max_count skal proceduren returnere værdien 5:

  max_count [list [list "Pizza nr. 9" 5] [list "Pizza nr. 5" 2]]
Du kan med fordel benytte tcl's indbyggede foreach kommando til at gennemløbe de enkelte elementer i listen.

Opgave 2.3

Du skal nu skrive en ny procedure star_graph2 ved at modificere proceduren star_graph fra Opgave 2.1, således at det, selv ved mange tusinde bestillinger, er muligt grafisk at sammenligne antallet af bestillinger. I løsningen ønskes der vist 10 stjerner for retten med de fleste bestillinger.

Hint: Antallet af stjerner n der ønskes tegnes for en enkelt ret fås således ved formlen

n = 10 * count / max

hvor count er antallet af bestillinger for en bestemt ret og max er det maksimale antal gange en ret er bestilt. Proceduren star_graph2 kan med fordel benytte proceduren max_count til at returnere det maksimale antal gange en ret er bestilt.

Opgave 3 (10 procent) - Regulære udtryk

Opgave 3.1

Det regulære udtryk [0-9] matches af en forekomst af cifrene 0-9. Opskriv et regulært udtryk som matches af telefonnumre bestående af 8 cifre.

Telefonnumrene 26122212 og 21332223 skal således matche det regulære udtryk.

Opgave 3.2

Skriv en tcl-procedure pz_check_phone til at undersøge om et telefonnummer har formen som beskrevet i Opgave 3.1. Hvis telefonnummeret, som overføres til proceduren som argument, har den rigtige form skal proceduren med det samme returnere til stedet hvorfra proceduren kaldes. Ellers skal proceduren returnere en fejlbesked til brugerens web-browser ved brug af AOLservers indbyggede kommando ns_return, hvorefter programmet skal stoppe ved et kald til tcl-kommandoen exit.

Til at undersøge om telefonnummeret har den rigtige form skal du benytte tcl-kommandoen regexp.

Opgave 4 (20 procent) - SQL

Datamodellen for restaurantbestillingssystemet består af tre tabeller:
  1. store - indeholder data om restauranterne (butikkerne)
  2. dish - indeholder data om retterne der tilbydes af de enkelte restauranter
  3. dish_order - indeholder bestillinger knyttet til de enkelte restauranter
For at simplificere systemet antages det i det følgende at der til hver bestilling er knyttet netop en ret. Datamodellen er i form af et E/R-diagram angivet nedenfor. De firkantede kasser angiver entiteterne (tabellerne) og de ovale cirkler angiver attributter (felter) tilknyttet de enkelte tabeller. Herudover er en-til-mange relationer angivet med kragefødder.

Tabellerne store og dish og tre Oracle sekvencer til generering af unikke numre er oprettet ved følgende SQL kommandoer:

  create table store (
    id integer primary key,
    name varchar(400) not null,
    pic_url varchar(400),
    address varchar(400)
  );

  create table dish (
    id integer primary key,
    store_id references store,
    name varchar(400) not null
  );

  create sequence store_id_seq start with 10;
  create sequence dish_id_seq start with 10;
  create sequence order_id_seq start with 10;
Følgende SQL insert kommandoer kan efterfølgende antages at være udført:
  insert into store (id, name, pic_url, address)
  values (1, 'Didim Pizza Pasta og Grill', 'http://didim.dk/didim.jpg',
	  'Vibevej 35, 2400 Kbh. NV. Tlf. 38100571 - Så har vi det klar når De kommer');

  insert into dish (id, store_id, name)
  values (1, 1, 'Nr 9: Pepperoni. Kr. 38,-');

  insert into dish (id, store_id, name)
  values (2, 1, 'Nr 24: Hawaii. Kr. 38,-');

  insert into dish (id, store_id, name)
  values (3, 1, 'Nr 36: Didim Bøf. Kr. 40,-');

Opgave 4.1

Vis resultatet af at udføre følgende select kommando:
  select id, name from dish;

Opgave 4.2

Opskriv en SQL kommando til at oprette tabellen dish_order. Tabellen skal have fem kolonner: Brug Oracles not null-constraint til at udtrykke at felterne order_date og phone skal være udfyldte.

Opgave 4.3

Skriv to SQL kommandoer til oprettelse af følgende bestillinger i tabellen dish_order - givet de tidligere indsættelser i tabellerne store og dish:
  1. Nr 9: Pepperoni hos Didim Pizza Pasta og Grill til telefonnummer 26122212 den 5. januar, 2001 (2001-01-05)
  2. Nr 36: Didim Bøf hos Didim Pizza Pasta og Grill til telefonnummer 21332223 den 6. januar, 2001 (2001-01-06)

Opgave 4.4

Konstruer en SQL kommando til udtrækning af endnu ikke afhentede restaurantbestillinger (deleted_p = 'f') til restauranten ``Didim Pizza Pasta og Grill''. Følgende kolonner skal indgå i resultatet: order_date, dish_id, name (dvs. dish.name), phone, order_id (dvs. dish_order.id). Rækkerne ønskes sorteret efter bestillingsdatoen.

Opgave 4.5

Konstruer en SQL kommando til udtrækning af statistik omkring bestilte retter for restauranten ``Didim Pizza Pasta og Grill''. Kolonnerne i resultatet skal være name (dvs. dish.name) og count (dvs. antallet af bestillinger af den pågældende ret). Både afhentede og ikke-afhentede bestillinger skal indgå i statistikken.

Hint: SQL kommandoen skal hente data fra de to tabeller dish og dish_order og desuden benytte SQL's group by konstruktion.

Opgave 5 (30 procent) - Web-service

Til konstruktion af tcl-filerne som implementerer restaurantbestillingssystemet er der opstillet to tilstandsdiagrammer til at underbygge henholdsvis kundefunktioner og restaurantadministrationsfunktioner. Kasserne i tilstandsdiagrammerne repræsenterer web-sider (implementeret som tcl-filer), pilene uden annoteringer repræsenterer links og de annoterede pile repræsenterer transaktioner. Tilstandsdiagrammet for kundefunktionerne er som følger:

Opgave 5.1

Nedenfor følger en skabelon til en procedure pz_return_page, som har til formål at kunne benyttes som ramme til at returnere HTML-sider til brugere. Ved at proceduren tager som argument en database handle db og en store identifier store_id kan proceduren, ved hjælp af databaseforespørgsler, konstruere en passende header og footer for siderne:
  proc pz_return_page { db store_id body } {

    # Fetch store name and address from the database
    set query " ... "
    set selection [ns_db 1row $db $query]
    set_variables_after_query

    # return a page (see Opgave 1.2)
    ns_return 200 text/html "
      <html>
         ... $name ... $body ... $address ...
      </html>"
  }
Opskriv tcl-koden for definitionen af tcl-variablen query, således at det efter set_variables_after_query kommandoen bliver muligt at henvise til restaurantens navn og adresse ved brug af tcl-variablerne name og address.

Bemærk: Det er ikke meningen at du her skal opskrive den returnerende HTML-kode; dette har du tilnærmelsesvis gjort i Opgave 1.2.

Opgave 5.2

I denne opgave skal du konstruere filen order.tcl som udføres når en kunde afgiver en ordre. Vi vil ikke her beskæftige os med at konstruere filen index.tcl, men det kan antages at formvariabler phone, dish_id og store_id overgives til filen order.tcl (se iøvrigt Opgave 1.1).

Filen order.tcl skal indsætte i databasen en bestilling af retten dish_id for restauranten store_id, foretaget af en person med telefonnummer phone. Datoen for bestillingen skal også indgå i de data der indsættes i databasen.

Bemærk: Det kan yderligere antages at procedurerne pz_check_id og pz_check_phone kan benyttes til at sikre at en streng indeholder henholdsvis et tal og et telefonnummer. Herudover kan proceduren pz_return_page benyttes til at returnere en ``Tak for din bestilling''-side til kunden med mulighed for at klikke sig tilbage til index.tcl-siden og bestille flere retter. Filen index.tcl forventer store_id overført som formvariabel.

Opgave 5.3

I denne opgave skal du konstruere dele af filen statistics.tcl på baggrund af skabelonen nedenfor:
  # Set the form variable 'store_id'
  set_form_variables
  pz_check_id $store_id

  set db [ns_db gethandle]

  set query " -- DIT SVAR PÅ OPGAVE 4.5 MED store_id = 1
              -- ERSTATTET AF store_id = '$store_id' "

  set selection [ns_db select $db $query]

  set dishes [list]
  while { [ns_db getrow $db $selection] } {

      # Set the tcl-variables name and count to the dish name and the
      # number of orders of this particular dish
      set_variables_after_query
  
      # APPEND A PAIR (ACTUALLY A LIST) OF name AND count
      # TO THE LIST dishes
      ##A## ...
  }

  set graph [star_graph2 $dishes]

  # RETURN A PAGE TO THE USER
  ##B## ...
Opgaven består i at opskrive kode for programpunkterne ##A## og ##B## i skabelonen ovenfor.

Opgave 5.4

Tilstandsdiagrammet til restaurantadministrationsfunktionerne ser ud som følger:

For at simplificere systemet kræves der ikke password til at tilgå restaurantadministrationsfunktionerne.

Betragt følgende skabelon til filen admin.tcl:

  # set and check the form variable 'store_id'
  set_form_variables
  pz_check_id $store_id

  set db [ns_db gethandle]

  set query " -- DIT SVAR PÅ OPGAVE 4.4 MED store_id = 1
	      -- ERSTATTET AF store_id = '$store_id' "

  set selection [ns_db select $db $query]

  # INITIALIZE THE TCL VARIABLE body
  ##A## ...
  while { [ns_db getrow $db $selection] } {
      set_variables_after_query

      # The variables order_date, name, phone, 
      # and order_id are now set

      # APPEND AN ORDER TO THE PAGE BODY
      ##B## ...
  }

  # APPEND SOME HTML-CODE TO body
  ##C## ...

  set header "
    <meta http-equiv=Refresh 
     content=\"5;url=http://hug.it.edu:8002/pz/admin.tcl?store_id=$store_id\">"

  pz_return_page_with_header $db $store_id $header "
    <h2>Bestillinger</h2>
    <a href=index.tcl?store_id=$store_id>Hovedsiden</a> :: Bestillingsoversigt
    <hr>
    $body"
Opskriv kode for programpunkterne ##A##, ##B## og ##C## i skabelonen ovenfor. Husk blandt andet et link til filen delete.tcl, som forventer formvariablerne store_id og order_id.

Bemærk: Koden for filen admin.tcl benytter en modificeret udgave af proceduren pz_return_page, som tillader et meta-tag at blive indsat i HTML-headeren; meta-tagget der her benyttes sørger for at browseren genloader siden admin.tcl hver femte sekund, således at restaurantejerne straks bliver opmærksomme på nye bestillinger!

Opgave 5.5

Konstruer tcl-filen delete.tcl.

Bemærk: Bestillingen skal ikke slettes fra databasen, da dette vil ødelægge statistikmuligheden. Istedet skal feltet deleted_p opdateres til 't' ved brug af SQL's update kommando. Endelig skal tcl-filen omdirigere brugeren til admin.tcl-filen ved brug af tcl-kommandoen ns_returnredirect.


mael@it.edu