d6
- Inlämningsdatum Inget inlämningsdatum
- Poäng 1
- Lämnar in en filuppladdning
- Filtyper py och txt
Table of Contents
1 Första uppgiften: webbgränssnitt i HTML via php, GET
I denna uppgift ska vi skapa ett webbgränssnitt för att söka kortaste vägen mellan två ord. För att göra det behöver du skapa en webbkatalog där webbservern kan komma åt ditt program.
1.1 Skapa webbkatalog och sätt rättigheter
logga in på kth:s student-shell server i en terminal med ssh
byt användarndarnamn nedan mot ditt KTH login
ssh dittanvändarnamn@student-shell.sys.kth.se
Skapa en folder public_php det är där webbservern kommer att leta efter ditt program.
mkdir public_php
Sätt de läsrättigheter som behövs i katalogen.
fs sa public_php system:anyuser none fs sa public_php httpd-course read
Skapa en html-fil h.html i katalogen och kolla att den skapats
echo "<h1>TESTING TESTING</h1>" > public_php/h.html ls public_php/
Webbserverns adress är course-dd1321-vt19.csc.kth.se
Testa om du kan se html-filen genom att skriva följande url (byt alba mot ditt khtlogin) i en webbläsare.
https://course-dd1321-vt19.csc.kth.se/~alba/h.html
1.2 Skapa ett html-formulär d63.html
Läs på om html-form Links to an external site.. Skapa en fil d63.html som innehåller följande html-form Links to an external site.
<head>
<meta charset="UTF-8">
</head>
<h1>välkommen till ordvägsletaren</h1> <form action="./d63.php" method="get"> First word:<br> <input type="text" name="first" value="gul"><br> Last word:<br> <input type="text" name="last" value="sot"><br><br> <input type="submit" value="Submit"> </form>
1.3 Skapa en php-fil d63.php
Läs på om form handling Links to an external site.. Skapa en fil d63.php som tar hand om värdena man matar in i html-formuläret och anropar ett python-program med värdena.
Observera att allting inom <?php … ?> är kod i programmeringsspråket php.
<?php
putenv("PYTHONIOENCODING=utf-8"); // Konfigurera webbservern att använda UTF-8
putenv("LANG=C.UTF-8");
error_reporting(E_ALL); // Sätt på php-felmeddelanden
ini_set('display_errors', 1);
$first = $_GET["first"]; // Spara GET-parametrarna
$last = $_GET["last"];
$first = preg_replace ("/[^a-zåäö]/", "", $first); // Byt, av säkerhetskäl ut icke-bokstäver
$last = preg_replace ("/[^a-zåäö]/", "", $last); // i GET-parametrarna
?>
<html>
<body>
första ordet: <?php echo $first; ?><br>
andra ordet: <?php echo $last; ?>
<h2>python program</h2>
<?php
// Kör pythonprogrammet och skriv ut (echo) i webbläsaren
echo shell_exec("python3 d63.py " . $first . " " . $last . " 2>&1 ");
?>
</body>
</html>
kodförklaringar som du inte behöver redogöra för
<head> |
Anger att html-filen är teckenkodad i utf-8 format. |
<?php | Anger för webbservern att nu följer kod skriven i programmeringsspråket php |
?> | Anger slut på php-koden |
putenv("PYTHONIOENCODING=utf-8"); putenv("LANG=C.UTF-8"); |
Konfigurerar webservern att använda utf-8 istället för latin-1 |
error_reporting(E_ALL); ini_set('display_errors', 1); |
Gör så att php-felmeddelanden syns i webbläsaren. Normalt vill man inte visa pinsamma php-felmeddelanden hos kunden |
$first = preg_replace ("/[^a-zåäö]/", "", $first); $last = preg_replace ("/[^a-zåäö]/", "", $last); |
Reuljärt uttryck som matchar allt som inte är små svenska bokstäver och byter dem mot ingenting. Detta är ett viktigt skydd så att man inte injicerar skadliga kodanrop i GET-parametern. |
shell_exec | Kör ditt pythonprogram på servern |
2>&1 | Skriver både stdout och stderr (felmeddelanden) i webbläsaren |
1.4 Skapa ett pythonprogram d63.py
Skapa ett pythonprogram som tar två kommandoradsparametrar. Kommandoradsparametrar hanteras av modulen sys som måste importeras. Parametrarna hamnar i vektorn sys.argv där första värdet (index 0) är namnet på programmet som körs.
import sys if len(sys.argv) < 3: print("parametrar saknas") sys.exit() else: print("parameter 1 är", sys.argv[1], " och parameter 2 är", sys.argv[2])
1.5 Kopiera programmen till public_php
Kopiera filerna d63.html, d63.php och d63.py till php-katalogen public_php på kth. Om du har skapat filenarna lokalt kan du kopiera manuellt med kommandot scp i terminalen.
scp d63.* dittanvändarnamn@student-shell.sys.kth.se:~/public_php
Det finns alternativa sätt att kopiera filer med ett grafiskt gränssnitt som kan vara enklare att använda. För windows finns t.ex. programmet WinSCP och för OSX t.ex. cyberduck.
1.6 Testa i webbläsare
Skriv in följande url i webbläsaren (byt ut användarnamn mot ditt kth-login)
https://course-dd1321-vt19.csc.kth.se/~dittanvändarnamn/d63.html
tryck på submit då kommer d63.php att visas. Det kan vara en god idé att bokmärka (lägga till favoriter) den här sidan eftersom du kommer att återkomma till den flera gånger.
Notera att parametrarna syns i url:en efter ett ? åtskilda med ett &-tecken.
https://course-dd1321-vt19.csc.kth.se/~dittanvändarnamn/d63.php?first=gul&last=sot
Prova ändra gul till vit i urlen och ladda om sidan ( ↻ ) i webbläsaren.
1.7 åäö
Programmen ovan ska vara kodade i utf-8 format för att det hela ska fungera. Om något av programmen är sparade i latin-1 (iso-8859-1) så kan man få svårfelsökta fel.
Man kan med programmet file se vilket format en fil har genom att i terminalen skriva
file public_php/d63.php
Det går att konvertera filformat via terminalen med programmet iconv Links to an external site. men det är smidigare att spara i olika filformat med en bra editor. Sitter ni på ubuntudatorerna i datasalarna och jobbar så blir det rätt från början.
1.8 Lägg upp lab d3 på public_php
I lab d3 skulle du skriva ett huvudprogram som tog två kommandoradsargument och hittade kortaste vägen mellan två ord.
ur lab d3:
Skriv ett huvudprogram som tar två kommandoradsargument (via sys.argv) som start- och slutord och skriver ut vägen, om det finns en väg, och ett annat meddelande om det inte finns en väg.
Kopiera programkoden och klistra in i filen d63.py på servern. Prova att i html-formuläret söka efter kortaste vägen från sur till söt. Om det fungerar är du klar med första uppgiften.
---------------------------------------------------------------------------------------------------------------------------
2 Andra uppgiften - återvänd till lab p1
Kopiera lab p1.py som du gjorde tidigare till d61.py, du ska nu ändra och förbättra programmet. Kopiera eventuellt också DD1321.htm
Efter kursslut är schemat tomt. Byt ut schemalänken i p1.py mot den här statiska som visar hela kursen från början till slut:
url = "https://cloud.timeedit.net/kth/web/public01/ri167499X83Z0QQ5Z16g3YZ5yQ086Y75Z0QgQY6Q5027o5p0ll55qnW67XwWa9WP16bx6ju.html"
Om den inte heller fungerar, ladda ned en färdig schemafil, DD1321.htm, härifrån kurssidorna och lägg den i samma katalog som p1.py.
2.1 Reguljära uttryck
Programmet använder sig av reguljära uttryck för att rader i html-filen och plocka ut delar av den matchade raden.
Uttryck inom parentes i de matchade raderna kan fås ut med group
reg_expr_w = re.compile('.*?td.*?class.*?headline.*?> *([MTOFL][åinorä]) *20[12][0-9]-0?(1?[0-9])-0?([123]?[0-9])<.*weekin.*> *(v.*?)</', re.I) reg_expr_d = re.compile('.*?td.*?class.*?headline.*?>([A-Z][a-z]) *20[12][0-9]-0?(1?[0-9])-0?([123]?[0-9])</') reg_expr_t = re.compile('.*?td +id="time.*?>(.+?)</td') reg_expr_i = re.compile('.*?td.*?class.*?column[0-1].*?>(.*?)</td', re.I)
Markera vilka rader nedan som matchas av de reguljära uttrycken ovan och ringa in vad parenteserna (group) matchar.
01 <tr> 02 <td class="weekColumn t">v 45</td> 03 <td colspan="2" class="headline leftRounded t ">Må 2019-11-04<span class="weekin"> v 45</span></td> 04 <td class="headline rightRounded t" colspan="4"></td> 05 </tr> 06 <tr data-id="274455" class="rr clickable2" onclick="openRes(274455, this, 'time274455','&step=0')"> 07 <td class="modifiedRecentlyTd"></td> 08 <td id="time274455" class="time tt c-1 " title="ID 274455">15:00 - 17:00</td> 09 <td class="column0 columnLine nw c-1">Föreläsning</td> 10 <td class="column1 columnLine nw c-1">DD1321 Helklass</td> 11 <td class="column0 columnLine nw c-1">DD1321</td> 12 <td class="column1 columnLine nw c-1">H1</td> 13 </tr>
2.2 Rätta buggar
2.2.1 Sparas alla attribut?
Gå igenom if-satserna parse_url_file och kontollera om alla matchingar sparas undan i medlemsvariabler.
2.2.2 Bugg veckonummer skrivs inte alltid ut.
Veckonummer påträffas bara en gång per vecka och inte för varje schemahändelse. Öppna "DD1321.htm" i en webbläsare så ser du. Inför en veckovariabel innan for-loopen. När veckonumret påträffas, tilldela inte medlemsvariabeln direkt utan tilldela den nya veckovariabeln istället. På sätt kommer veckovariabeln att "komma ihåg" vilken vecka det är. När en ny schemahändelse skapas, använd veckovariabeln för att sätta rätt vecka.
2.2.3 Ibland skrivs inte dagen ut
Det här är samma typ av bugg som veckan, den inträffar när det är två schemahänelser samma dag. Lös den på samma sätt genom att införa nya lokala variabler.
2.2.4 Bugg första schemahändelsen skrivs inte ut.
När man kommer till tiden skapas en ny schemahändelse och den sparas undan i en vektor/lista med append. Men för första schemahändelsen så har man inte hunnit läsa in vad som skulle hända. Som du ser ovan så kommer tiden innan vad som ska hända.
För att lösa detta problem ska vi använda oss av en tillståndsvariabel för att hålla reda på om det är första gången vi når en schema-tid eller inte.
newEntry = False for j, line in enumer...
Lägg till en if-sats där man läser in tiden och lägger till schemahändelsen
if newEntry == True: vek.append(qq) qq = ...
Iden är att första gången man stöter på tiden så ska schemahändelsen inte läggas till. Det återstår för dig att sätta newEntry till True på lämpligt ställe i koden samt att fixa till så att alla data för första tillfället syns.
2.2.5 förbättring, söka i klassen
I klassen finns en __contains__ metod som möjliggör att man kan använda in för att söka i objektet (jämför hashtabell if "eva" in htable). För närvarande så letar koden bara i en av medlemsvariablerna. Lägg till fler if-satser i __contains__ metod så att programmet letar i alla medlemsvariablerna.
2.3 parse_command_line_args
Studera funktionen parse_command_line_args. Koden använder ett paket get_opt för att hantera kommandoradsargument.
opts, rest = getopt.getopt(sys.argv[1:], "hc:u", ["help", "check=", "update"]) ... for option, value in opts: ...
Det som returneras är en dictionary där kommandoradsargument med bindestreck i läggs som nycklar och det som kommer direkt efter som värde. Det gör att det är möjligt att skriva flera kommandoradsargument och i olika ordning. Man kan också göra kortformer av olika options.
gcc använder getoptions.
gcc main.c -o main.exe gcc -o main.exe main.c
Vad returnerar parse_command_line_args för slags datatyp? Uppdatera funktionskommentaren OUT och beskriv vad funktionen returnerar.
2.4 search_data
Första parametern what är något som kommer från parse_command_line_args. Vad? Uppdatera funktionskommentaren IN och beskriv what.
Koden letar efter what i listan dataset. Om du ändrat i __contains__-metoden så bör du kunna söka efter olika saker. Prova t.ex.
python3 d63.py -c Föreläsning python3 d63.py -c sning python3 d63.py -c D3
3 Tredje uppgiften: Använd REST-api för att få ut schemat.
I denna uppgift ska du hämta schema för kursen via ett REST-API.
3.1 Prova REST-api i webbläsare
Läs igenom den korta dokumentationen för KTH:s REST-api
Man använder sig av GET-parametrar för att hämta data från en REST-server. Datat som returneras är javascript objekt i JSON-format Links to an external site.
Prova hämta följande url i en webläsare, notera GET-parametern startTime som måste stavas precis så.
https://www.kth.se/social/api/schema/v2/course/DD1321?startTime=2021-01-14
Ovanstående kalenderlänk ger ett tomt schema så fort nästa kursomgång startat. För att se aktuell kursomgång ge dagens datum som startTime eller utelämna den parametern: https://www.kth.se/social/api/schema/v2/course/DD1321
Resultatet är svårt att förstå vid en första anblick
{"url": "https://www.kth.se/social/course/DD1321/calendar/", "entries": [{"info": null, "start": "2019-01-16 08:00:00", "end": "2019-01-16 10:00:00", "title": "F\u00f6rel\u00e4sning", "url": "https://www.kth.se/social/course/DD1321/subgroup/ht-2018-tilpro18/event/215047/", "group": "", "type_name": {"en": "Lecture", "sv": "F\u00f6rel\u00e4sning"}, "type": "Frl", "locations": [{"url": "https://www.kth.se/places/room/id/4a5e9e92-d7aa-486d-8503-b91b331d1662", "name": "B2"}]}, {"info": null, "start": "2019-01-16 10:00:00", "end": "2019-01-16 12:00:00", "title": "Laboration", "url": "https://www.kth.se/social/course/DD1321/subgroup/ht-2018-tilpro18/event/215106/", "group": "", "type_name": {"en": "Laboratory", "sv": "Laboration"}, "type": "OVR", "locations": [{"url": "https://www.kth.se/places/room/id/6fd6bf98-7e86-4b60-b069-24f2cb451af7", "name": "5O1Spe"}]}, {"info": null, "start": "2019-01-17 08:00:00", "end": "2019-01-17 10:00:00", "title": "\u00d6vning", "url": "https://www.kth.se/social/course/DD1321/subgroup/ht-2018-tilpro18/event/215108/", "group": "", "type_name": {"en": "Exercise", "sv": "\u00d6vning"}, "type": "Ovn", "locations": [{"url": "https://www.kth.se/places/room/id/2446d268-83f7-4072-ab1f-dbb2fff723f8", "name": "V01"}, {"url": "https://www.kth.se/places/room/id/d3133a70-8732-48dc-bd33-90a9fe47fbb4", "name": "V11"}, {"url": "https://www.kth.se/places/room/id/8890efb8-9ac9-4d9d-bb64-817d5f0da58c", "name": "V12"}]}, {"info": null, "start": "2019-01-17 10:00:00", "end": "2019-01-17 12:00:00", "title": "Laboration", "url": "https://www.kth.se/social/course/DD1321/subgroup/ht-2018-tilpro18/event/215107/", "group": "", "type_name": {"en": "Laboratory", "sv": "Laboration"}, "type": "OVR", "locations": [{"url": "https://www.kth.se/places/room/id/6fd6bf98-7e86-4b60-b069-24f2cb451af7", "name": "5O1Spe"}]}, {"info": null, "start": "2019-01-18 08:00:00", "end": "2019-01-18 10:00:00", "title": "F\u00f6rel\u00e4sning", "url": "https://www.kth.se/social/course/DD1321/subgroup/ht-2018-tilpro18/event/215109/", "group": "", "type_name": {"en": "Lecture", "sv": "F\u00f6rel\u00e4sning"}, "type": "Frl", "locations": [{"url": "https://www.kth.se/places/room/id/5b3e2d05-8da8-405e-ac62-7960935363ab", "name": "V1"}]}]}
3.2 Pythonprogram som hämtar via REST-api
Python har stöd för att hämta data från en REST-server. Skriv in och kör följande kod i ett program d6.py
import json import urllib import urllib.request import sys schemaurl = "https://www.kth.se/social/api/schema/v2/course/" course = "DD1321" start = "?startTime=2021-01-14"
end = "&endTime=2021-02-01"
schemaurl += course + start + end request_data = urllib.request.urlopen(schemaurl).read() # hämtar data från REST-servern utf_data = request_data.decode('utf-8') # översätter u00f6 -> ö datastruktur = json.loads(utf_data) # lägger in i en pythonstruktur print (datastruktur)
Spara utskriften i en fil och döp den till data.js. Om man vid varje "," gör en indenterad ny rad så skönjer man en datastruktur med dictionaries {} och listor []. Ni kan även prova skriva ut med pprint
import pprint
pprint.pprint(datastruktur)
{'entries': [{'info': None, 'start': '2019-01-16 08:00:00', 'group': '', 'locations': [{'name': 'B2', 'url': 'https://www.kth.se/places/room/id/4a5e9e92-d7aa-486d-8503-b91b331d1662'}], 'type': 'Frl', 'title': 'Föreläsning', 'end': '2019-01-16 10:00:00', 'url': 'https://www.kth.se/social/course/DD1321/subgroup/ht-2018-tilpro18/event/215047/', 'type_name': {'sv': 'Föreläsning', 'en': 'Lecture'} }, {'info': None, 'start': '2019-01-16 10:00:00', 'group': '', 'locations': [{'name': '5O1Spe', 'url': 'https://www.kth.se/places/room/id/6fd6bf98-7e86-4b60-b069-24f2cb451af7'}], 'type': 'OVR', 'title': 'Laboration', 'end': '2019-01-16 12:00:00', 'url': 'https://www.kth.se/social/course/DD1321/subgroup/ht-2018-tilpro18/event/215106/', 'type_name': {'sv': 'Laboration', 'en': 'Laboratory'} }, {'info': None, 'start': '2019-01-17 08:00:00', 'group': '', 'locations': [{'name': 'V01', 'url': 'https://www.kth.se/places/room/id/2446d268-83f7-4072-ab1f-dbb2fff723f8' }, {'name': 'V11', 'url': 'https://www.kth.se/places/room/id/d3133a70-8732-48dc-bd33-90a9fe47fbb4' }, {'name': 'V12', 'url': 'https://www.kth.se/places/room/id/8890efb8-9ac9-4d9d-bb64-817d5f0da58c'}], 'type': 'Ovn', 'title': 'Övning', 'end': '2019-01-17 10:00:00', 'url': 'https://www.kth.se/social/course/DD1321/subgroup/ht-2018-tilpro18/event/215108/', 'type_name': {'sv': 'Övning', 'en': 'Exercise'} }, ...
Datastrukturen består av en dictionary med två nycklar url och entries. Värdet i entries är en lista som består av flera dictionaries.
Testa följande print-satser för att få en känsla för hur man når olika data i datastrukturen. Testa också att ändra och se hur resultatet blir. Döp om variabeln datastruktur till ett variabelnamn som beskriver vad för slags data variablen innehåller.
print(datastruktur["entries"][2]) print(datastruktur["entries"][2]["start"]) print(datastruktur["entries"][2]["end"]) print(datastruktur["entries"][2]["title"]) for x in datastruktur["entries"][2]["locations"]: print(x["name"])
3.3 Programmera d6.py
Skriv ett program d6.py som tar 1-3 parametrar. Den första parametern anger vilken kurs man vill söka schema för. Den andra och tredje parametern är optional och anger startdatum respektive slutdatum.
Programmet ska skriva ut datum, tid, plats och vad som schemalagts.
Så här skulle en programutskrift kunna se ut:
$ python3 d6.py DD1321 2019-01-14
Schema för DD1321 2019-01-16 08-10 Föreläsning B2 2019-01-16 10-12 Laboration 5O1Spe 2019-01-17 08-10 Övning i V01 V11 V12 2019-01-17 10-12 Laboration 5O1Spe 2019-01-18 08-10 Föreläsning V1
Prova kör programmet med andra kurser, t.ex. kurser som årets 1:or läser denna period.
3.3.1 reguljära uttryck som kontrollerar indata
REST-api:et är mycket petigt på hur datum matas in. Skriv ett reguljärt uttryck som matchar datum på formatet YYYY-MM-DD och ger ett förklarande felmeddelande om man anger datum på fel format.
Frivillig uppgift
Ett frivilligt alternativ till att ge felmeddelande är att med hjälp av matchingen göra om datumet till rätt format så att följande fungerar och ger samma resultat
python3 d6.py DD1321 19-01-14
python3 d6.py DD1321 190114
python3 d6.py DD1321 2019-01-14
Använd grupperingar i det reguljära uttrycket och sätt ihop dem, typ:
s = "20" + m.group(1) + '-' + m.group(2) + '-' + m.group(3)
3.3.2 Tips: skriva ut på samma rad
print() skriver alltid ut ett nyradstecken. Om man vill skriva flera print-satser på samma rad så använder man end=""
T.ex.
print("Telnr", end=" ") print("Adam ", end="\t") print(123114, end="") print()
3.3.3 Tips: datum och tid
Använd split och strängmanipulering för att få ut datum och tid i det format du vill ha.
a, b = "Eva 123156".split() print(a, b[:3])
4 Redovisning
Vid redovisning ska du
4.1 Förklara vad GET-parametrar är
4.2 Visa hur ditt webbgränssnitt till lab d3 fungerar
4.3 Redovisa dina funktionskommentarer i p1.py/d61.py
4.4 Köra ditt schemaprogram och visa schema för olika kurser
4.5 Förklara dina reguljära uttryck