Compare commits

...

45 Commits

Author SHA1 Message Date
4ecaa1a216 Add final test material 2022-12-20 21:49:28 -04:00
fcef1277f3 Merge remote-tracking branch 'origin/master' 2022-12-07 23:49:32 -04:00
8507082995 Assignment 6 skeleton 2022-12-07 23:49:22 -04:00
e091a825ce Add quiz 3 content, and quiz 3 practice 2022-11-23 10:09:24 -04:00
8501f2f473 Update tests 2022-11-18 16:30:31 -04:00
bfdc5dc93b Add assignment 5 content 2022-11-18 16:28:38 -04:00
d81425a781 Remove virtual env
I only need the venv on 2 machines and its easier just to have a requirements.txt
2022-11-16 10:00:27 -04:00
62f9f2677d Add lab 17 stuff 2022-11-16 09:59:43 -04:00
95918841b0 Add lab 16 stuff 2022-11-16 09:59:08 -04:00
419e67f80a Add tests 2022-11-04 17:53:24 -03:00
38135762f6 Add exploding bunny rabbit 2022-11-04 17:53:17 -03:00
5c03647a4a Add assignment 4 2022-11-04 15:07:21 -03:00
3ee1155e1f Merge remote-tracking branch 'origin/master' 2022-11-04 15:03:31 -03:00
efb36f83fc Mark executables binary 2022-11-04 15:02:58 -03:00
d28bd6ed1c Remove shebang 2022-11-02 10:02:34 -03:00
45c5b94e63 Add some lab 15 contents 2022-11-02 10:02:15 -03:00
0f1e8d5e36 Add run configs for lab 14 2022-11-02 09:43:37 -03:00
57405f5aac Fix python venv 2022-11-02 09:15:23 -03:00
8500f351fc Add lab 15 skeleton 2022-11-02 09:15:03 -03:00
268e3b6a47 Add journal 2022-11-01 23:49:37 -03:00
a50f49d2c8 Add python venv 2022-10-31 10:10:52 -03:00
fb1a0435c1 Update Lab 14 venv 2022-10-31 10:10:27 -03:00
490552f9bd Remove pyc files 2022-10-31 10:03:04 -03:00
07b1461635 Update gitignore 2022-10-31 10:02:49 -03:00
d5c907859b Add lab 14 content and journal skeleton 2022-10-31 09:58:30 -03:00
f45f47b572 Add lab 13 skeleton 2022-10-31 09:58:03 -03:00
4daea2121b Update gitignore to include python files 2022-10-31 09:57:29 -03:00
e18f58c0d0 Finish quiz 2 2022-10-26 10:18:05 -03:00
9a62c4918f Add quiz 2 content 2022-10-26 10:10:09 -03:00
8f668a032f Update lab 12 skeleton 2022-10-25 11:15:01 -03:00
243b14b247 Add lab 12 skeleton 2022-10-24 10:08:40 -03:00
869e89188d Add lab 11 skeleton 2022-10-24 08:44:46 -03:00
ee767d8067 Make body function work with all tests 2022-10-21 00:31:35 -03:00
29d579b8ac Add extra test 2022-10-21 00:31:21 -03:00
542510f5d0 Add Assignment 3 solution 2022-10-20 23:39:33 -03:00
66b3b590b7 Add lab 10 journal 2022-10-18 16:25:33 -03:00
f22f52eebe Rename journals to have date in them 2022-10-18 12:09:08 -03:00
138644d870 Change the way posts are named 2022-10-17 10:38:02 -03:00
29dd89f729 Add lab 10 content 2022-10-17 10:24:22 -03:00
5fe9b2c913 Add gitattributes for line endings 2022-10-12 18:42:14 -03:00
b70d452341 Correct tags 2022-10-12 11:11:47 -03:00
eefd47a0fc Update lab 9 content 2022-10-12 11:10:43 -03:00
f62c83ddbe Rename function in text 2022-10-12 09:15:48 -03:00
76d1fb6c53 Add lab 9 content 2022-10-12 09:04:35 -03:00
585fb2e9e8 Work on A2 2022-10-07 11:41:22 -03:00
141 changed files with 6501 additions and 12 deletions

6
.gitattributes vendored Normal file
View File

@ -0,0 +1,6 @@
# All line endings should be unix
* text eol=lf
*.png binary
*.jpg binary
*.exe binary

11
.gitignore vendored
View File

@ -1 +1,10 @@
*.bak
# Racket related files
*.bak
# Javascript related files
.nyc_output
# Python related files
.pytest_cache
.coverage
__pycache__

1
assignments/A2/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
A2temp.rkt

43
assignments/A2/A2.rkt Normal file
View File

@ -0,0 +1,43 @@
#lang racket
(require xml)
(define (load-xexpr path)
(xml->xexpr (document-element (read-xml (open-input-file path)))))
(define (load-rubrics path)
(define rubrics (load-xexpr path))
(define (get-rubrics rubrics)
(cond
[(null? rubrics) '()]
[(list? rubrics) (append (get-rubrics (car rubrics)) (get-rubrics (cdr rubrics)))]
[else (list rubrics)]))
(get-rubrics rubrics))
; test for load-rubrics
(module+ test
(require rackunit)
(define rubrics (load-rubrics "rubrics.xml"))
(check-equal? (length rubrics) 5)
(for ([elt rubrics])
(check-equal? (first elt) 'rubric)))
; test for assoc*
(module+ test
(define test-list '(1 [keep 2] 3 [keep 4] [keep 5] 6))
(check-equal? (assoc* 'keep test-list) 2)
(check-equal? (assoc* 'discard test-list) #f))
(define (rubric-name rubric)
(assoc* 'name (second rubric)))
(module+ test
(check-equal?
(sort (map rubric-name rubrics) string<=?)
'("JavaScript Assignment" "Journal Entry" "Octave Assignment" "Python Assignment"
"Racket assignment")))

View File

@ -0,0 +1,9 @@
#lang racket
(require xml)
(define (load-xexpr path)
(xml->xexpr (document-element (read-xml (open-input-file path)))))
(require explorer)
(define data (load-xexpr "rubrics.xml"))
(explore data)

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,92 @@
[
[
[
{
"id": "20091117232137.GA7669@griffis1.net",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/unb/.list.notmuch/cur/1324645067.000017.mbox:2,"
],
"timestamp": 1258500098,
"date_relative": "2009-11-17",
"tags": [
"unread"
],
"headers": {
"Subject": "[notmuch] archive",
"From": "Aron Griffis <agriffis@n01se.net>",
"To": "notmuch <notmuch@notmuchmail.org>",
"Date": "Tue, 17 Nov 2009 18:21:38 -0500"
},
"body": [
{
"id": 1,
"content-type": "text/plain",
"content-disposition": "inline",
"content": "Just subscribed, I'd like to catch up on the previous postings,\nbut the archive link seems to be bogus?\n\nThanks,\nAron\n\n"
}
]
},
[
[
{
"id": "yunzl6kd1w0.fsf@aiko.keithp.com",
"match": true,
"excluded": false,
"filename": [
"/home/bremner/Maildir/unb/.list.notmuch/cur/1324645067.000028.mbox:2,S"
],
"timestamp": 1258509871,
"date_relative": "2009-11-17",
"tags": [],
"headers": {
"Subject": "Re: [notmuch] archive",
"From": "Keith Packard <keithp@keithp.com>",
"To": "Aron Griffis <agriffis@n01se.net>, notmuch <notmuch@notmuchmail.org>",
"Date": "Tue, 17 Nov 2009 18:04:31 -0800"
},
"body": [
{
"id": 1,
"content-type": "text/plain",
"content": "On Tue, 17 Nov 2009 18:21:38 -0500, Aron Griffis <agriffis@n01se.net> wrote:\n\n> Just subscribed, I'd like to catch up on the previous postings,\n> but the archive link seems to be bogus?\n\nYeah, the archive appears broken and will need to wait until Carl\narrives in Barcelona to get fixed.\n\n--\nkeith.packard@intel.com\n\n\n\n"
}
]
},
[
[
{
"id": "87iqd8qgiz.fsf@yoom.home.cworth.org",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/unb/.list.notmuch/cur/1324645067.000042.mbox:2,"
],
"timestamp": 1258539732,
"date_relative": "2009-11-18",
"tags": [
"unread"
],
"headers": {
"Subject": "Re: [notmuch] archive",
"From": "Carl Worth <cworth@cworth.org>",
"To": "Keith Packard <keithp@keithp.com>, Aron Griffis <agriffis@n01se.net>, notmuch <notmuch@notmuchmail.org>",
"Date": "Wed, 18 Nov 2009 02:22:12 -0800"
},
"body": [
{
"id": 1,
"content-type": "text/plain",
"content": "On Tue, 17 Nov 2009 18:04:31 -0800, Keith Packard <keithp@keithp.com> wrote:\n> On Tue, 17 Nov 2009 18:21:38 -0500, Aron Griffis <agriffis@n01se.net> wrote:\n> \n> > Just subscribed, I'd like to catch up on the previous postings,\n> > but the archive link seems to be bogus?\n> \n> Yeah, the archive appears broken and will need to wait until Carl\n> arrives in Barcelona to get fixed.\n\nFixed it in transit in Frankfurt---with only moments to spare on my\nbattery and no outlets in sight.\n\nThanks for the report, Aron. And welcome to notmuch!\n\n-Carl (who wants to reply to a lot more mail, but will have to wait\n until later for that)\n\n"
}
]
},
[]
]
]
]
]
]
]
]

View File

@ -0,0 +1,346 @@
[
[
[
{
"id": "20101026131106.3791.46291.reportbug@i7",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/unb/.list.debian-devel/cur/1288099996.28935.pivot.cs.unb.ca:2,S"
],
"timestamp": 1288098666,
"date_relative": "2010-10-26",
"tags": [],
"headers": {
"Subject": "Bug#601455: general: can't stop daemon using /etc/init.d/foo stop when disabled via /etc/default/foo",
"From": "Mathias Kub <git@makubi.at>",
"To": "Debian Bug Tracking System <submit@bugs.debian.org>",
"Reply-To": "Mathias Kub <git@makubi.at>, 601455@bugs.debian.org",
"Date": "Tue, 26 Oct 2010 15:11:06 +0200"
},
"body": [
{
"id": 1,
"content-type": "text/plain",
"content": "Package: general\nSeverity: minor\nTags: patch\n\nWhen I try to stop a daemon after I disabled it in /etc/default/foo, I get an error-message that I can not stop it, because it is disabled.\n\nShouldn't I be able to stop it even if I disabled it first?\n\nThis happens if you disabled a daemon but didn't restart after that.\n\nIn my case it's MPD, I even tried it with icecast2, but I think this applies to more than these packages.\n\n-----------\nuser@sys:~$ sudo /etc/init.d/mpd stop\nNot stopping MPD: disabled by /etc/default/mpd. failed!\n\nuser@sys:~$ sudo /etc/init.d/icecast2 stop\nicecast2 daemon disabled - read /etc/default/icecast2.\n-----------\n\nE.g. in /etc/init.d/icecast2 I changed\n- if [ \"$ENABLE\" != \"true\" ]; then\nto\n+ if [ \"$ENABLE\" != \"true\" ] && [ \"$1\" != \"stop\" ]; then\n\nBest regards,\nMathias Kub\n\n-- System Information:\nDebian Release: lenny\n\n\n\n-- \nTo UNSUBSCRIBE, email to debian-devel-REQUEST@lists.debian.org\nwith a subject of \"unsubscribe\". Trouble? Contact listmaster@lists.debian.org\nArchive: http://lists.debian.org/20101026131106.3791.46291.reportbug@i7\n\n\n"
}
]
},
[
[
{
"id": "20111017041202.GA6506@elie.hsd1.il.comcast.net",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/unb/.list.debian-devel/cur/1318824935.H635656P9975.tesseract.cs.unb.ca:2,"
],
"timestamp": 1318824722,
"date_relative": "2011-10-17",
"tags": [
"unread"
],
"headers": {
"Subject": "Bug#601455: general: can't stop daemon using /etc/init.d/foo stop when disabled via /etc/default/foo",
"From": "Jonathan Nieder <jrnieder@gmail.com>",
"To": "Mathias Kub <git@makubi.at>",
"Cc": "601455@bugs.debian.org",
"Reply-To": "Jonathan Nieder <jrnieder@gmail.com>, 601455@bugs.debian.org",
"Date": "Sun, 16 Oct 2011 23:12:02 -0500"
},
"body": [
{
"id": 1,
"content-type": "text/plain",
"content-disposition": "inline",
"content": "tags 601455 - patch\nretitle 601455 multiple, annoyingly different ways to disable an init script\nquit\n\nHi Mathias,\n\nMathias Kub wrote:\n\n> When I try to stop a daemon after I disabled it in /etc/default/foo,\n> I get an error-message that I can not stop it, because it is\n> disabled.\n>\n> Shouldn't I be able to stop it even if I disabled it first?\n\nYes, I agree that this is a bug. Nowadays the appropriate way\nto disable an init script is to remove the 'S' links without removing\nthe 'K' links, for example by running\n\n\tupdate-rc.d foo disable\n\nUnfortunately:\n\n 1. That is not as well known is it ought to be. For example, section\n 4.6.3. \"Restricting access to some server services\"[1] of\n debian-reference could be clarified to emphasize this method.\n\n 2. Many packages seem to provide ENABLE/DISABLE variables in\n /etc/default/foo, providing a confusing red herring for this\n task --- a second method which does not work nearly as well,\n as you pointed out.\n\n 3. The tempting \"update-rc.d foo remove\" (which removes the 'K'\n links, too) might _seem_ to work, except that the next time the\n foo package is upgraded, the service is back again.\n\nOne possible way to move forward would be to write a patch to the\ndebian reference and any other pertinent documentation to address (1)\nand (3) and (once consensus that this is a good idea is reached) to\nfile bugs requesting removal of the ENABLE/DISABLE vars to address (2),\nblocking this bug by them. When the last such variable is eliminated\nfrom the default conffiles in /etc/default, this bug could be closed.\n\nA complicating factor is that the sysadmin may already have customized\nsome ENABLE/DISABLE settings and a move like this should not override\ntheir settings. So perhaps packages should stop advertising the\nENABLE/DISABLE vars in /etc/default/<package>, but continue to respect\nthem when set.\n\nSane?\n\nThanks,\nJonathan\n\n[1] http://www.debian.org/doc/manuals/debian-reference/ch04.en.html#_restricting_access_to_some_server_services\n\n\n\n-- \nTo UNSUBSCRIBE, email to debian-devel-REQUEST@lists.debian.org\nwith a subject of \"unsubscribe\". Trouble? Contact listmaster@lists.debian.org\nArchive: http://lists.debian.org/20111017041202.GA6506@elie.hsd1.il.comcast.net\n\n"
}
]
},
[
[
{
"id": "handler.s.C.131882474613866.transcript@bugs.debian.org",
"match": true,
"excluded": false,
"filename": [
"/home/bremner/Maildir/unb/.list.debian-devel/cur/1318824952.H424691P9980.tesseract.cs.unb.ca:2,S"
],
"timestamp": 1318824912,
"date_relative": "2011-10-17",
"tags": [],
"headers": {
"Subject": "Processed: Re: general: can't stop daemon using /etc/init.d/foo stop when disabled via /etc/default/foo",
"From": "owner@bugs.debian.org (Debian Bug Tracking System)",
"To": "Jonathan Nieder <jrnieder@gmail.com>",
"Cc": "general for {601455} <debian-devel@lists.debian.org>",
"Date": "Mon, 17 Oct 2011 04:15:12 +0000"
},
"body": [
{
"id": 1,
"content-type": "text/plain",
"content": "Processing commands for control@bugs.debian.org:\n\n> tags 601455 - patch\nBug #601455 [general] general: can't stop daemon using /etc/init.d/foo stop when disabled via /etc/default/foo\nRemoved tag(s) patch.\n> retitle 601455 multiple, annoyingly different ways to disable an init script\nBug #601455 [general] general: can't stop daemon using /etc/init.d/foo stop when disabled via /etc/default/foo\nChanged Bug title to 'multiple, annoyingly different ways to disable an init script' from 'general: can't stop daemon using /etc/init.d/foo stop when disabled via /etc/default/foo'\n> quit\nStopping processing here.\n\nPlease contact me if you need assistance.\n-- \n601455: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=601455\nDebian Bug Tracking System\nContact owner@bugs.debian.org with problems\n\n\n-- \nTo UNSUBSCRIBE, email to debian-devel-REQUEST@lists.debian.org\nwith a subject of \"unsubscribe\". Trouble? Contact listmaster@lists.debian.org\nArchive: http://lists.debian.org/handler.s.C.131882474613866.transcript@bugs.debian.org\n\n"
}
]
},
[]
]
]
],
[
{
"id": "87efrfvc5p.fsf@iris.silentflame.com",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/tethera/cur/1504989708.H253980P5302.fethera.tethera.net:2,S"
],
"timestamp": 1504989618,
"date_relative": "September 09",
"tags": [
"signed"
],
"headers": {
"Subject": "Bug#601455: Steps towards a patch to document disabling a daemon upon installation",
"From": "Sean Whitton <spwhitton@spwhitton.name>",
"To": "601455@bugs.debian.org",
"Cc": "debian-devel@lists.debian.org",
"Reply-To": "Sean Whitton <spwhitton@spwhitton.name>, 601455@bugs.debian.org",
"Date": "Sat, 09 Sep 2017 13:40:18 -0700"
},
"body": [
{
"id": 1,
"content-type": "multipart/signed",
"content": [
{
"id": 2,
"content-type": "text/plain",
"content": "Hello,\n\nThis is what I have so far; it is certainly inadequate. CCing -devel\nfor help answering my technical questions about this patch.\n\n> @@ -537,6 +537,21 @@ and in your ``postrm``\n> update-rc.d package remove\n> fi\n> \n> +The default behaviour is to enable autostarting your package's daemon.\n> +If the daemon should not be autostarted unless the local administrator\n> +has explicitly requested this, use instead add to your ``postinst``\n> +script\n> +\n> +::\n> +\n> + update-rc.d package defaults\n> + update-rc.d package disable\n> +\n> +An older practice, which should not be used, was to include a line\n> +like ``DISABLED=yes`` in the package's ``/etc/default`` file. The\n> +package's init script would not start the service until the local\n> +system administrator changed this to ``DISABLED=no``, or similar.\n> +\n> Note that if your package changes runlevels or priority, you may have to\n> remove and recreate the links, since otherwise the old links may\n> persist. Refer to the documentation of ``update-rc.d``.\n\n1. Is the 'should not' for the /etc/default practice too strong? I\n don't know an efficient way to find out how many packages this would\n make buggy. But given that we have very strong reasons against the\n old practice, we might want to use a 'should not' regardless.\n\n2. Do we need to include any text saying *why* the /etc/default practice\n is a bad idea? I couldn't come up with a succinct way to state it.\n In general, I think we can err on the side of not including the text,\n since we have policy bugs that document the reasons.\n\n3. The maintscript snippet I have added is not right because it will\n disable the daemon every time the package is updated. Unfortunately,\n the postinst doesn't know whether this is a new installation, or an\n upgrade.\n\n An alternative is to require the package maintainer to set the\n correct LSB headers and systemd unit file configuration values such\n that the daemon is not autostarted (in the former case, setting the\n daemon not to be started at any runlevel). But I think this would\n prevent the local system administrator from enabling the service with\n a simple `update-rc.d package enable`, which is the whole point of\n all this.\n\n I looked at dh_installinit(8) and update-rc.d(8) and I couldn't get\n them to generate a postinst that does what I want. It seems you're\n expected to use all three of these:\n\n dh_systemd_enable --no-enable\n dh_systemd_start --no-start\n dh_installinit --no-start\n\n but then after a reboot, a sysvinit system will start the daemon,\n AFAICT.\n\n-- \nSean Whitton\n"
},
{
"id": 3,
"content-type": "application/pgp-signature",
"filename": "signature.asc",
"content-length": 832
}
]
}
]
},
[]
],
[
{
"id": "20170910074844.ncsj7ihhdmqsxob7@bongo.bofh.it",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/tethera/cur/1505053420.H579482P8345.fethera.tethera.net:2,S"
],
"timestamp": 1505029724,
"date_relative": "September 10",
"tags": [
"signed"
],
"headers": {
"Subject": "Bug#601455: Steps towards a patch to document disabling a daemon upon installation",
"From": "md@Linux.IT (Marco d'Itri)",
"To": "debian-policy@lists.debian.org",
"Cc": "601455@bugs.debian.org, debian-devel@lists.debian.org",
"Reply-To": "Marco d'Itri <md@Linux.IT>, 601455@bugs.debian.org",
"Date": "Sun, 10 Sep 2017 09:48:44 +0200"
},
"body": [
{
"id": 1,
"content-type": "multipart/signed",
"content-disposition": "inline",
"content": [
{
"id": 2,
"content-type": "text/plain",
"content-disposition": "inline",
"content": "On Sep 09, Sean Whitton <spwhitton@spwhitton.name> wrote:\n\n> 1. Is the 'should not' for the /etc/default practice too strong? I\nNo, because it cannot be supported in a sane way by systemd units.\nIt should even be \"must not\".\n\n-- \nciao,\nMarco\n"
},
{
"id": 3,
"content-type": "application/pgp-signature",
"filename": "signature.asc",
"content-length": 659
}
]
}
]
},
[]
],
[
{
"id": "20170910111619.ak3co2pxx7w7ybwq@fatal.se",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/tethera/cur/1505042623.H930419P7859.fethera.tethera.net:2,S"
],
"timestamp": 1505042179,
"date_relative": "September 10",
"tags": [],
"headers": {
"Subject": "Bug#601455: Steps towards a patch to document disabling a daemon upon installation",
"From": "Andreas Henriksson <andreas@fatal.se>",
"To": "Sean Whitton <spwhitton@spwhitton.name>",
"Cc": "601455@bugs.debian.org, debian-devel@lists.debian.org",
"Reply-To": "Andreas Henriksson <andreas@fatal.se>, 601455@bugs.debian.org",
"Date": "Sun, 10 Sep 2017 13:16:19 +0200"
},
"body": [
{
"id": 1,
"content-type": "text/plain",
"content-disposition": "inline",
"content": "Hello Sean Whitton,\n\nThanks for your work on this. Hopefully you'll find something in my\ncomments inlined below of any use...\n\nOn Sat, Sep 09, 2017 at 01:40:18PM -0700, Sean Whitton wrote:\n> Hello,\n> \n> This is what I have so far; it is certainly inadequate. CCing -devel\n> for help answering my technical questions about this patch.\n> \n> > @@ -537,6 +537,21 @@ and in your ``postrm``\n> > update-rc.d package remove\n> > fi\n> > \n> > +The default behaviour is to enable autostarting your package's daemon.\n> > +If the daemon should not be autostarted unless the local administrator\n> > +has explicitly requested this, use instead add to your ``postinst``\n> > +script\n> > +\n> > +::\n> > +\n> > + update-rc.d package defaults\n> > + update-rc.d package disable\n\nI can't help myself but repeat that I'd prefer seeing more passive\nwording eg. instead of \"instead add to your postinst\" use something\nlike \"the postinst should contain\" + a footnote about this normally\nbeing added by dh_...\nManually written maintainer scripts should be avoided and I've seen\npeople being \"fooled\" by taking policy literally before. (Maybe this\ndeserves a section of its own?)\n\n> > +\n> > +An older practice, which should not be used, was to include a line\n> > +like ``DISABLED=yes`` in the package's ``/etc/default`` file. The\n> > +package's init script would not start the service until the local\n> > +system administrator changed this to ``DISABLED=no``, or similar.\n> > +\n> > Note that if your package changes runlevels or priority, you may have to\n> > remove and recreate the links, since otherwise the old links may\n> > persist. Refer to the documentation of ``update-rc.d``.\n> \n> 1. Is the 'should not' for the /etc/default practice too strong?\n\nNot in my opinion, no.\n\n> I don't know an efficient way to find out how many packages this\n> would make buggy. But given that we have very strong reasons\n> against the old practice, we might want to use a 'should not'\n> regardless.\n\nAny maintainer being hit by policy extremists have two options:\n\n1. Take the opportunity to fix the package to follow best pracises.\n2. Postpone by saying \"should not\" is not \"must not\" (and lower severity),\n plus \"patches welcome\" ofcourse.\n\nI think that's good enough.\n\n> \n> 2. Do we need to include any text saying *why* the /etc/default practice\n> is a bad idea? I couldn't come up with a succinct way to state it.\n> In general, I think we can err on the side of not including the text,\n> since we have policy bugs that document the reasons.\n\nI don't think elaborating on all the ways something can be done\nincorrectly is nessessary. Should not be too hard for anyone interested\nin the reason to find out atleast one reason either by thinking it\nthrough by themselves or by googling for past discussions.\n\nIf anything I'd rather see helpful suggestions (in footnotes?) on how\na proper cleanup should be done. (Convert admin changes on upgrades,\nuse debian/*.maintscript to rm_conffile)\n\n> \n> 3. The maintscript snippet I have added is not right because it will\n> disable the daemon every time the package is updated. Unfortunately,\n> the postinst doesn't know whether this is a new installation, or an\n> upgrade.\n> \n> An alternative is to require the package maintainer to set the\n> correct LSB headers and systemd unit file configuration values such\n> that the daemon is not autostarted (in the former case, setting the\n> daemon not to be started at any runlevel). But I think this would\n> prevent the local system administrator from enabling the service with\n> a simple `update-rc.d package enable`, which is the whole point of\n> all this.\n> \n> I looked at dh_installinit(8) and update-rc.d(8) and I couldn't get\n> them to generate a postinst that does what I want. It seems you're\n> expected to use all three of these:\n> \n> dh_systemd_enable --no-enable\n> dh_systemd_start --no-start\n> dh_installinit --no-start\n> \n> but then after a reboot, a sysvinit system will start the daemon,\n> AFAICT.\n\nhttps://bugs.debian.org/cgi-bin/bugreport.cgi?bug=709384\n\nRegards,\nAndreas Henriksson\n\n"
}
]
},
[]
],
[
{
"id": "22966.37496.665485.820932@chiark.greenend.org.uk",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/tethera/cur/1505137301.H848769P17164.fethera.tethera.net:2,S"
],
"timestamp": 1505137272,
"date_relative": "September 11",
"tags": [],
"headers": {
"Subject": "Bug#601455: Steps towards a patch to document disabling a daemon upon installation",
"From": "Ian Jackson <ijackson@chiark.greenend.org.uk>",
"To": "Sean Whitton <spwhitton@spwhitton.name>",
"Cc": "601455@bugs.debian.org, debian-devel@lists.debian.org",
"Reply-To": "Ian Jackson <ijackson@chiark.greenend.org.uk>, 601455@bugs.debian.org",
"Date": "Mon, 11 Sep 2017 14:41:12 +0100"
},
"body": [
{
"id": 1,
"content-type": "text/plain",
"content": "Sean Whitton writes (\"Steps towards a patch to document disabling a daemon upon installation\"):\n...\n> [draft policy text]\n...\n> > +The default behaviour is to enable autostarting your package's daemon.\n> > +If the daemon should not be autostarted unless the local administrator\n> > +has explicitly requested this, use instead add to your ``postinst``\n> > +script\n> > +\n> > +::\n> > +\n> > + update-rc.d package defaults\n> > + update-rc.d package disable\n\nThis has a bug: after the first rune, but before this second, starting\nthe daemon is enabled. (This is a regression compared to the previous\napproach.)\n\nTo make this work correctly, I think we need a new update-rc.d\nmechanism which provides, in one go, the equivalent of\n update-rc.d DAEMON defaults && update-rc.d DAEMON disable\n\nSomething like\n update-rc.d DAEMON add-disabled\nmaybe.\n\n> > +An older practice, which should not be used, was to include a line\n> > +like ``DISABLED=yes`` in the package's ``/etc/default`` file. The\n> > +package's init script would not start the service until the local\n> > +system administrator changed this to ``DISABLED=no``, or similar.\n...\n> 1. Is the 'should not' for the /etc/default practice too strong? I\n> don't know an efficient way to find out how many packages this would\n> make buggy. But given that we have very strong reasons against the\n> old practice, we might want to use a 'should not' regardless.\n\nOn sysvinit systems, using update-rc.d disable/defaults are rather\nmore awkward:\n\n * Enabling and disabling cannot, in practice, be conveniently made\n without using the update-rc.d tool.\n\n * Enabling and disabling generates a tremendous amount of noise in\n /etc (especially visible when using etckeeper).\n\n> 2. Do we need to include any text saying *why* the /etc/default practice\n> is a bad idea? I couldn't come up with a succinct way to state it.\n> In general, I think we can err on the side of not including the text,\n> since we have policy bugs that document the reasons.\n\nHow about this text:\n\n Setting a value in /etc/default/PACKAGE is nowadays troublesome\n because supporting that pattern is very hard due to inflexibility in\n systemd, which is usually the default init system.\n\nThis also makes it clear that this pattern is perfectly fine if for\nany reason the package does not support systemd.\n\n> 3. The maintscript snippet I have added is not right because it will\n> disable the daemon every time the package is updated. Unfortunately,\n> the postinst doesn't know whether this is a new installation, or an\n> upgrade.\n\nThis should also be fixed with a new update-rc.d rune.\n\n> I looked at dh_installinit(8) and update-rc.d(8) and I couldn't get\n> them to generate a postinst that does what I want. It seems you're\n> expected to use all three of these:\n> \n> dh_systemd_enable --no-enable\n> dh_systemd_start --no-start\n> dh_installinit --no-start\n> \n> but then after a reboot, a sysvinit system will start the daemon,\n> AFAICT.\n\nI can't speak to the behaviour of systemd, but I think the\n\n update-rc.d add-disabled\n\noperation I propose would, for sysvinit systems, do the follow:\n\n1. Are there already rc*.d links for DAEMON ? If so, do nothing.\n\n2. If not, create them in the way that defaults && disable\n would have done.\n\nIan.\n\n"
}
]
},
[]
],
[
{
"id": "87poaupkdc.fsf@iris.silentflame.com",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/tethera/cur/1505324860.H792966P2543.fethera.tethera.net:2,S"
],
"timestamp": 1505324447,
"date_relative": "September 13",
"tags": [
"signed"
],
"headers": {
"Subject": "Bug#601455: Steps towards a patch to document disabling a daemon upon installation",
"From": "Sean Whitton <spwhitton@spwhitton.name>",
"To": "601455@bugs.debian.org",
"Cc": "debian-devel@lists.debian.org",
"Reply-To": "Sean Whitton <spwhitton@spwhitton.name>, 601455@bugs.debian.org",
"Date": "Wed, 13 Sep 2017 10:40:47 -0700"
},
"body": [
{
"id": 1,
"content-type": "multipart/signed",
"content": [
{
"id": 2,
"content-type": "text/plain",
"content": "control: block 601455 by 857452\n\nOn Mon, Sep 11 2017, Ian Jackson wrote:\n\n> This should also be fixed with a new update-rc.d rune.\n\nThank you, Ian and Felipe, for your feedback.\n\nI think the right thing is to wait on #857452.\n\n-- \nSean Whitton\n"
},
{
"id": 3,
"content-type": "application/pgp-signature",
"filename": "signature.asc",
"content-length": 832
}
]
}
]
},
[]
],
[
{
"id": "handler.s.B601455.150532489815922.transcript@bugs.debian.org",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/tethera/new/1505324862.H799240P2547.fethera.tethera.net"
],
"timestamp": 1505325065,
"date_relative": "September 13",
"tags": [
"unread"
],
"headers": {
"Subject": "Processed: Re: Steps towards a patch to document disabling a daemon upon installation",
"From": "owner@bugs.debian.org (Debian Bug Tracking System)",
"To": "Sean Whitton <spwhitton@spwhitton.name>",
"Cc": "debian-policy@lists.debian.org, pkg-systemd-maintainers@lists.alioth.debian.org",
"Date": "Wed, 13 Sep 2017 17:51:05 +0000"
},
"body": [
{
"id": 1,
"content-type": "text/plain",
"content-disposition": "inline",
"content": "Processing control commands:\n\n> block 601455 by 857452\nBug #601455 [debian-policy] Standardize how to disable an init script\nBug #522163 [debian-policy] standard for disabling daemons in /etc/default\nBug #661496 [debian-policy] Standardize how to disable an init script\n601455 was not blocked by any bugs.\n601455 was not blocking any bugs.\nAdded blocking bug(s) of 601455: 857452\n522163 was not blocked by any bugs.\n522163 was not blocking any bugs.\nAdded blocking bug(s) of 522163: 857452\n661496 was not blocked by any bugs.\n661496 was not blocking any bugs.\nAdded blocking bug(s) of 661496: 857452\n\n-- \n522163: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=522163\n601455: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=601455\n661496: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=661496\nDebian Bug Tracking System\nContact owner@bugs.debian.org with problems\n\n"
}
]
},
[]
],
[
{
"id": "20170914190335.flyi6434gedo3vkz@cloud",
"match": false,
"excluded": false,
"filename": [
"/home/bremner/Maildir/tethera/new/1505416300.H973951P10371.fethera.tethera.net"
],
"timestamp": 1505415815,
"date_relative": "September 14",
"tags": [
"unread"
],
"headers": {
"Subject": "Bug#601455: Steps towards a patch to document disabling a daemon upon installation",
"From": "Josh Triplett <josh@joshtriplett.org>",
"To": "601455@bugs.debian.org",
"Reply-To": "Josh Triplett <josh@joshtriplett.org>, 601455@bugs.debian.org",
"Date": "Thu, 14 Sep 2017 12:03:35 -0700"
},
"body": [
{
"id": 1,
"content-type": "text/plain",
"content-disposition": "inline",
"content": "On Mon, 11 Sep 2017 14:41:12 +0100 Ian Jackson <ijackson@chiark.greenend.org.uk> wrote:\n> Sean Whitton writes (\"Steps towards a patch to document disabling a daemon upon installation\"):\n> > [draft policy text]\n> > > +The default behaviour is to enable autostarting your package's daemon.\n> > > +If the daemon should not be autostarted unless the local administrator\n> > > +has explicitly requested this, use instead add to your ``postinst``\n> > > +script\n> > > +\n> > > +::\n> > > +\n> > > + update-rc.d package defaults\n> > > + update-rc.d package disable\n> \n> This has a bug: after the first rune, but before this second, starting\n> the daemon is enabled. (This is a regression compared to the previous\n> approach.)\n> \n> To make this work correctly, I think we need a new update-rc.d\n> mechanism which provides, in one go, the equivalent of\n> update-rc.d DAEMON defaults && update-rc.d DAEMON disable\n> \n> Something like\n> update-rc.d DAEMON add-disabled\n> maybe.\n\nI'd agree with this as a starting point, for setting a daemon-specific\ndefault. However, I also think we need a system-wide policy mechanism to\nallow the sysadmin to say \"start things by default\" or \"don't start\nthings by default\". Similar to systemd's \"preset\" mechanism, and ideally\nusing that under systemd, but providing the same level of control for\nnon-systemd init systems.\n\n> > > +An older practice, which should not be used, was to include a line\n> > > +like ``DISABLED=yes`` in the package's ``/etc/default`` file. The\n> > > +package's init script would not start the service until the local\n> > > +system administrator changed this to ``DISABLED=no``, or similar.\n> ...\n> > 1. Is the 'should not' for the /etc/default practice too strong? I\n> > don't know an efficient way to find out how many packages this would\n> > make buggy. But given that we have very strong reasons against the\n> > old practice, we might want to use a 'should not' regardless.\n> \n> On sysvinit systems, using update-rc.d disable/defaults are rather\n> more awkward:\n> \n> * Enabling and disabling cannot, in practice, be conveniently made\n> without using the update-rc.d tool.\n\nWhy is this an issue? We have update-rc.d to do this. \n\n> * Enabling and disabling generates a tremendous amount of noise in\n> /etc (especially visible when using etckeeper).\n\nThis seems like an artifact of sysvinit's choice of storage format for\nrunlevel configuration. (And I never found that noise particularly\nexcessive in etckeeper; it's a handful of symlink deletions/creations.)\n\n> > 2. Do we need to include any text saying *why* the /etc/default practice\n> > is a bad idea? I couldn't come up with a succinct way to state it.\n> > In general, I think we can err on the side of not including the text,\n> > since we have policy bugs that document the reasons.\n> \n> How about this text:\n> \n> Setting a value in /etc/default/PACKAGE is nowadays troublesome\n> because supporting that pattern is very hard due to inflexibility in\n> systemd, which is usually the default init system.\n> \n> This also makes it clear that this pattern is perfectly fine if for\n> any reason the package does not support systemd.\n\nWhich (among many other reasons) is precisely why we shouldn't use this\ntext, because many people have been very reasonably arguing for the\nelimination of /etc/default and *especially* mechanisms like\n\"DISABLED=true\" in it for longer than systemd has existed.\n\n/etc/default is Debian-specific, and things like DISABLED=true break the\nability to *manually* start services. They also make it difficult to\nprogrammatically configure services, such as by dropping in a\nconfiguration .d file from a configuration package.\n\n"
}
]
},
[]
]
]
]
]
]

View File

@ -0,0 +1,186 @@
[
[
[
{
"id" : "87boejw7ll.fsf@tamas.ihs.ac.at",
"body" : [
{
"id" : 1,
"content-type" : "text/plain",
"content" : "Hi,\n\nI have a T430s with an ultrabay battery, which is discharged before the\nmain one. I was hoping I could balance the discharge between batteries\n--- I found http://www.thinkwiki.org/wiki/Talk:Code/tp-bat-balance but\nit needs tp_smapi which is not supported on this machine (AFAIK).\n\nIs there a way to balance battery discharging on this model? I would\nalso be happy if I could manually select which battery is discharged.\n\nBest,\n\nTamas\n-- \nThe linux-thinkpad mailing list home page is at:\nhttp://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad\n"
}
],
"timestamp" : 1354019462,
"filename" : [
"/home/bremner/Maildir/unb/.list.linux-thinkpad/cur/1354019706.H536188P5506.tesseract.cs.unb.ca:2,S"
],
"date_relative" : "2012-11-27",
"tags" : [],
"excluded" : false,
"match" : true,
"headers" : {
"Date" : "Tue, 27 Nov 2012 13:31:02 +0100",
"Reply-To" : "linux-thinkpad@linux-thinkpad.org",
"To" : "\"linux-thinkpad@linux-thinkpad.org\" <linux-thinkpad@linux-thinkpad.org>",
"Subject" : "[ltp] battery balancing on T430s?",
"From" : "Tamas Papp <tkpapp@gmail.com>"
}
},
[
[
{
"excluded" : false,
"match" : false,
"headers" : {
"Subject" : "Re: [ltp] battery balancing on T430s?",
"From" : "Frank Baumeister <baumeisterf@web.de>",
"Reply-To" : "linux-thinkpad@linux-thinkpad.org",
"Date" : "Tue, 27 Nov 2012 14:31:03 +0100",
"To" : "linux-thinkpad@linux-thinkpad.org"
},
"body" : [
{
"content" : "Perhaps there is a way with TLP. Linrunner.de/en/tlp/tlp.html\n\n\n\nTamas Papp <tkpapp@gmail.com> schrieb:\n\n>Hi,\n>\n>I have a T430s with an ultrabay battery, which is discharged before the\n>main one. I was hoping I could balance the discharge between batteries\n>--- I found http://www.thinkwiki.org/wiki/Talk:Code/tp-bat-balance but\n>it needs tp_smapi which is not supported on this machine (AFAIK).\n>\n>Is there a way to balance battery discharging on this model? I would\n>also be happy if I could manually select which battery is discharged.\n>\n>Best,\n>\n>Tamas\n\n\n\n\n-- \nDiese Nachricht wurde von meinem Android-Mobiltelefon mit K-9 Mail gesendet.\n-- \nThe linux-thinkpad mailing list home page is at:\nhttp://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad\n",
"content-type" : "text/plain",
"id" : 1
}
],
"id" : "88b04c58-95b0-4633-aaf3-a611cf8c7176@email.android.com",
"tags" : [
"unread"
],
"filename" : [
"/home/bremner/Maildir/unb/.list.linux-thinkpad/cur/1354023304.H68786P5655.tesseract.cs.unb.ca:2,"
],
"date_relative" : "2012-11-27",
"timestamp" : 1354023063
},
[
[
{
"body" : [
{
"content" : "Thanks -- I am already using TLP, but I don't think that it offers this\nfunctionality.\n\nOn Tue, Nov 27 2012, Frank Baumeister <baumeisterf@web.de> wrote:\n\n> Perhaps there is a way with TLP. Linrunner.de/en/tlp/tlp.html\n>\n>\n>\n> Tamas Papp <tkpapp@gmail.com> schrieb:\n>\n>>Hi,\n>>\n>>I have a T430s with an ultrabay battery, which is discharged before the\n>>main one. I was hoping I could balance the discharge between batteries\n>>--- I found http://www.thinkwiki.org/wiki/Talk:Code/tp-bat-balance but\n>>it needs tp_smapi which is not supported on this machine (AFAIK).\n>>\n>>Is there a way to balance battery discharging on this model? I would\n>>also be happy if I could manually select which battery is discharged.\n>>\n>>Best,\n>>\n>>Tamas\n>\n>\n>\n>\n> -- \n> Diese Nachricht wurde von meinem Android-Mobiltelefon mit K-9 Mail gesendet.\n\n-- \nThe linux-thinkpad mailing list home page is at:\nhttp://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad\n",
"content-type" : "text/plain",
"id" : 1
}
],
"id" : "878v9nw4cy.fsf@tamas.ihs.ac.at",
"tags" : [
"unread"
],
"timestamp" : 1354023661,
"date_relative" : "2012-11-27",
"filename" : [
"/home/bremner/Maildir/unb/.list.linux-thinkpad/cur/1354023901.H934938P5672.tesseract.cs.unb.ca:2,"
],
"excluded" : false,
"match" : false,
"headers" : {
"Subject" : "Re: [ltp] battery balancing on T430s?",
"From" : "Tamas Papp <tkpapp@gmail.com>",
"Date" : "Tue, 27 Nov 2012 14:41:01 +0100",
"Reply-To" : "linux-thinkpad@linux-thinkpad.org",
"To" : "linux-thinkpad@linux-thinkpad.org"
}
},
[]
]
]
],
[
{
"timestamp" : 1354522521,
"date_relative" : "2012-12-03",
"filename" : [
"/home/bremner/Maildir/unb/.list.linux-thinkpad/cur/1354522806.H600217P26331.tesseract.cs.unb.ca:2,"
],
"tags" : [
"unread"
],
"id" : "CAOLOJ0xO1PoxUdRZYJDoZUfFaTu5r1yiDtSXEEXD_8YhzEU+3A@mail.gmail.com",
"body" : [
{
"content" : "you may take a look at this: http://savannah.nongnu.org/projects/battwd.\nIt can works with several batteries (the only bad is that, at the\nmoment, it's alpha code).\n\nOn 27 November 2012 13:31, Tamas Papp <tkpapp@gmail.com> wrote:\n> Hi,\n>\n> I have a T430s with an ultrabay battery, which is discharged before the\n> main one. I was hoping I could balance the discharge between batteries\n> --- I found http://www.thinkwiki.org/wiki/Talk:Code/tp-bat-balance but\n> it needs tp_smapi which is not supported on this machine (AFAIK).\n>\n> Is there a way to balance battery discharging on this model? I would\n> also be happy if I could manually select which battery is discharged.\n>\n> Best,\n>\n> Tamas\n> --\n> The linux-thinkpad mailing list home page is at:\n> http://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad\n-- \nThe linux-thinkpad mailing list home page is at:\nhttp://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad\n",
"content-type" : "text/plain",
"id" : 1
}
],
"headers" : {
"From" : "alfon <alfon.gz@gmail.com>",
"Subject" : "Re: [ltp] battery balancing on T430s?",
"To" : "linux-thinkpad@linux-thinkpad.org",
"Reply-To" : "linux-thinkpad@linux-thinkpad.org",
"Date" : "Mon, 03 Dec 2012 09:15:21 +0100"
},
"match" : false,
"excluded" : false
},
[
[
{
"tags" : [
"unread"
],
"date_relative" : "2012-12-03",
"filename" : [
"/home/bremner/Maildir/unb/.list.linux-thinkpad/cur/1354539302.H582083P27170.tesseract.cs.unb.ca:2,"
],
"timestamp" : 1354538957,
"body" : [
{
"content" : "Thanks. Does this support selecting which battery to discharge on the\nTP T430s? How? I can see that it can monitor the batteries, but for me\nthe problem is not monitoring but controlling the battery discharge.\n\nThe problem is that tp_smapi does not support this laptop yet, so there\nis no /sys/devices/platform/smapi/BAT0/force_discharge etc.\n\nOn Mon, Dec 03 2012, alfon <alfon.gz@gmail.com> wrote:\n\n> you may take a look at this: http://savannah.nongnu.org/projects/battwd.\n> It can works with several batteries (the only bad is that, at the\n> moment, it's alpha code).\n>\n> On 27 November 2012 13:31, Tamas Papp <tkpapp@gmail.com> wrote:\n>> Hi,\n>>\n>> I have a T430s with an ultrabay battery, which is discharged before the\n>> main one. I was hoping I could balance the discharge between batteries\n>> --- I found http://www.thinkwiki.org/wiki/Talk:Code/tp-bat-balance but\n>> it needs tp_smapi which is not supported on this machine (AFAIK).\n>>\n>> Is there a way to balance battery discharging on this model? I would\n>> also be happy if I could manually select which battery is discharged.\n>>\n>> Best,\n>>\n>> Tamas\n>> --\n>> The linux-thinkpad mailing list home page is at:\n>> http://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad\n\n-- \nThe linux-thinkpad mailing list home page is at:\nhttp://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad\n",
"content-type" : "text/plain",
"id" : 1
}
],
"id" : "87lidfwbaq.fsf@tamas.ihs.ac.at",
"match" : false,
"headers" : {
"Date" : "Mon, 03 Dec 2012 13:49:17 +0100",
"Reply-To" : "linux-thinkpad@linux-thinkpad.org",
"To" : "linux-thinkpad@linux-thinkpad.org",
"Subject" : "Re: [ltp] battery balancing on T430s?",
"From" : "Tamas Papp <tkpapp@gmail.com>"
},
"excluded" : false
},
[
[
{
"id" : "CAOLOJ0yKgyZf6w5+D1n8ogkgAD_chHpo29D122C7wSgTRzVs8w@mail.gmail.com",
"body" : [
{
"id" : 1,
"content-type" : "text/plain",
"content" : "No, for now, only works if the battery is under /sys/device, /proc or\nanother filesystem... sorry.\n\nOn 3 December 2012 13:49, Tamas Papp <tkpapp@gmail.com> wrote:\n> Thanks. Does this support selecting which battery to discharge on the\n> TP T430s? How? I can see that it can monitor the batteries, but for me\n> the problem is not monitoring but controlling the battery discharge.\n>\n> The problem is that tp_smapi does not support this laptop yet, so there\n> is no /sys/devices/platform/smapi/BAT0/force_discharge etc.\n>\n> On Mon, Dec 03 2012, alfon <alfon.gz@gmail.com> wrote:\n>\n>> you may take a look at this: http://savannah.nongnu.org/projects/battwd.\n>> It can works with several batteries (the only bad is that, at the\n>> moment, it's alpha code).\n>>\n>> On 27 November 2012 13:31, Tamas Papp <tkpapp@gmail.com> wrote:\n>>> Hi,\n>>>\n>>> I have a T430s with an ultrabay battery, which is discharged before the\n>>> main one. I was hoping I could balance the discharge between batteries\n>>> --- I found http://www.thinkwiki.org/wiki/Talk:Code/tp-bat-balance but\n>>> it needs tp_smapi which is not supported on this machine (AFAIK).\n>>>\n>>> Is there a way to balance battery discharging on this model? I would\n>>> also be happy if I could manually select which battery is discharged.\n>>>\n>>> Best,\n>>>\n>>> Tamas\n>>> --\n>>> The linux-thinkpad mailing list home page is at:\n>>> http://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad\n>\n> --\n> The linux-thinkpad mailing list home page is at:\n> http://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad\n-- \nThe linux-thinkpad mailing list home page is at:\nhttp://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad\n"
}
],
"timestamp" : 1354542140,
"date_relative" : "2012-12-03",
"filename" : [
"/home/bremner/Maildir/unb/.list.linux-thinkpad/cur/1354542304.H658190P27485.tesseract.cs.unb.ca:2,"
],
"tags" : [
"unread"
],
"excluded" : false,
"match" : false,
"headers" : {
"Subject" : "Re: [ltp] battery balancing on T430s?",
"From" : "alfon <alfon.gz@gmail.com>",
"Date" : "Mon, 03 Dec 2012 14:42:20 +0100",
"Reply-To" : "linux-thinkpad@linux-thinkpad.org",
"To" : "linux-thinkpad@linux-thinkpad.org"
}
},
[]
]
]
]
]
]
]
]
]
]

49
assignments/A3/message.js Normal file
View File

@ -0,0 +1,49 @@
function match(message, headers) {
for (let header in headers) {
if (message.headers[header] !== headers[header]) {
return false;
}
}
return true;
}
function body(message) {
if (message["body"] !== undefined) {
if (message["body"][0]["content-type"] === "text/plain") {
return message["body"][0]["content"];
}
/* istanbul ignore else */
if (message["body"][0]["content-type"] === "multipart/signed") {
return message["body"][0]["content"][0]["content"];
}
}
return undefined;
}
function string(message) {
let string = "";
if (message["headers"]["From"] !== undefined) {
string += "From: " + message["headers"]["From"] + "\n";
}
/* istanbul ignore else */
if (message["headers"]["Date"] !== undefined) {
string += "Date: " + message["headers"]["Date"] + "\n";
}
/* istanbul ignore else */
if (message["headers"]["Subject"] !== undefined) {
string += "Subject: " + message["headers"]["Subject"] + "\n";
}
if (message["headers"]["To"] !== undefined) {
string += "To: " + message["headers"]["To"] + "\n";
}
if (message["body"] !== undefined) {
string += "\n";
string += body(message);
}
return string;
}
exports.match = match;
exports.body = body;
exports.string = string;

View File

@ -0,0 +1,8 @@
let fs = require('fs');
function read_json_file(filename) {
let contents = fs.readFileSync(filename);
return JSON.parse(contents);
}
exports.read_json_file=read_json_file;

View File

@ -0,0 +1,128 @@
let read_json_file = require("../read_json_file.js").read_json_file;
let message = require("../message.js");
let testMsg = {"headers": {"Subject" : "lunch", "Date" : "now"}};
let otherMsg = {"headers": {"Subject" : "dinner", "Date" : "now"}};
describe("match",
function() {
it("message matches itself",
function () {
expect(message.match(testMsg,testMsg.headers)).toEqual(true);
});
it("message does not match if field changes",
function () {
expect(message.match(testMsg,otherMsg.headers)).toEqual(false);
});
it("match subset of fields",
function () {
expect(message.match(testMsg,{"Subject": "lunch"})).toEqual(true);
});
});
describe("message body",
function () {
it("unsigned message",
function () {
let message_objects=read_json_file("example1.json").flat(Infinity);
let msg = message_objects.filter((m) => message.match(m,{ "From": "Aron Griffis <agriffis@n01se.net>"}))[0];
expect(msg).not.toEqual(null);
expect(message.body(msg)).toEqual(`Just subscribed, I'd like to catch up on the previous postings,
but the archive link seems to be bogus?
Thanks,
Aron
`);
});
it("signed message",
function () {
let message_objects=read_json_file("example2.json").flat(Infinity);
let msg = message_objects.filter((m) => message.match(m,{"From": "md@Linux.IT (Marco d'Itri)"}))[0];
expect(msg).not.toEqual(null);
expect(message.body(msg)).toEqual(`On Sep 09, Sean Whitton <spwhitton@spwhitton.name> wrote:
> 1. Is the 'should not' for the /etc/default practice too strong? I
No, because it cannot be supported in a sane way by systemd units.
It should even be "must not".
--
ciao,
Marco
`);
});
it("no body",
function() {
expect(message.body(testMsg)).toEqual(undefined)
});
});
let fullHeaderMsg ={"headers": {
"Subject": "[notmuch] archive",
"From": "Aron Griffis <agriffis@n01se.net>",
"To": "notmuch <notmuch@notmuchmail.org>",
"Date": "Tue, 17 Nov 2009 18:21:38 -0500"
}};
describe("toString",
function () {
it("works with no body",
function () {
expect(message.string(testMsg)).toEqual(`Date: now
Subject: lunch
`);
});
it("full set of headers",
function() {
expect(message.string(fullHeaderMsg)).toEqual(`From: Aron Griffis <agriffis@n01se.net>
Date: Tue, 17 Nov 2009 18:21:38 -0500
Subject: [notmuch] archive
To: notmuch <notmuch@notmuchmail.org>
`);
});
});
describe("toString real data",
function () {
it("unsigned message",
function () {
let message_objects=read_json_file("example1.json").flat(Infinity);
let msg = message_objects.filter((m) => message.match(m,{ "From": "Aron Griffis <agriffis@n01se.net>"}))[0];
expect(msg).not.toEqual(null);
expect(message.string(msg)).toEqual(`From: Aron Griffis <agriffis@n01se.net>
Date: Tue, 17 Nov 2009 18:21:38 -0500
Subject: [notmuch] archive
To: notmuch <notmuch@notmuchmail.org>
Just subscribed, I'd like to catch up on the previous postings,
but the archive link seems to be bogus?
Thanks,
Aron
`);
});
it("signed message",
function () {
let message_objects=read_json_file("example2.json").flat(Infinity);
let msg = message_objects.filter((m) => message.match(m,{"From": "md@Linux.IT (Marco d'Itri)"}))[0];
expect(msg).not.toEqual(null);
expect(message.string(msg)).toEqual(`From: md@Linux.IT (Marco d'Itri)
Date: Sun, 10 Sep 2017 09:48:44 +0200
Subject: Bug#601455: Steps towards a patch to document disabling a daemon upon installation
To: debian-policy@lists.debian.org
On Sep 09, Sean Whitton <spwhitton@spwhitton.name> wrote:
> 1. Is the 'should not' for the /etc/default practice too strong? I
No, because it cannot be supported in a sane way by systemd units.
It should even be "must not".
--
ciao,
Marco
`);});
});

View File

@ -0,0 +1,13 @@
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.?(m)js"
],
"helpers": [
"helpers/**/*.?(m)js"
],
"env": {
"stopSpecOnExpectationFailure": false,
"random": true
}
}

178
assignments/A4/life.js Normal file
View File

@ -0,0 +1,178 @@
// ES2015 classes based on https://eloquentjavascript.net/2nd_edition/07_elife.html
class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
plus(other) {
return new Vector(this.x + other.x, this.y + other.y);
};
};
class Grid {
constructor (width, height) {
this.space = new Array(width * height);
this.width = width;
this.height = height;
};
isInside(vector) {
return vector.x >= 0 && vector.x < this.width &&
vector.y >= 0 && vector.y < this.height;
};
get(vector) {
return this.space[vector.x + this.width * vector.y];
};
set(vector, value) {
this.space[vector.x + this.width * vector.y] = value;
};
forEach(f, context) {
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
let value = this.space[x + y * this.width];
if (value != null)
f.call(context, value, new Vector(x, y));
}
}
};
}
let directions = {
"n": new Vector( 0, -1),
"ne": new Vector( 1, -1),
"e": new Vector( 1, 0),
"se": new Vector( 1, 1),
"s": new Vector( 0, 1),
"sw": new Vector(-1, 1),
"w": new Vector(-1, 0),
"nw": new Vector(-1, -1)
};
function randomElement(array) {
return array[Math.floor(Math.random() * array.length)];
}
let directionNames = "n ne e se s sw w nw".split(" ");
function BouncingCritter() {
this.direction = randomElement(directionNames);
};
BouncingCritter.prototype.act = function(view) {
if (view.look(this.direction) != " ")
this.direction = view.find(" ") || "s";
return {type: "move", direction: this.direction};
};
class View {
constructor(world, vector) {
this.world = world;
this.vector = vector;
}
look(dir) {
let target = this.vector.plus(directions[dir]);
if (this.world.grid.isInside(target))
return charFromElement(this.world.grid.get(target));
else
return "#";
}
findAll(ch) {
let found = [];
for (let dir in directions)
if (this.look(dir) == ch)
found.push(dir);
return found;
}
find(ch) {
let found = this.findAll(ch);
if (found.length == 0) return null;
return randomElement(found);
}
}
class World {
constructor(map, legend) {
let grid = new Grid(map[0].length, map.length);
this.grid = grid;
this.legend = legend;
map.forEach(function(line, y) {
for (let x = 0; x < line.length; x++)
grid.set(new Vector(x, y),
World.elementFromChar(legend, line[x]));
});
}
static elementFromChar(legend, ch) {
if (ch == " ")
return null;
let element = new legend[ch]();
element.originChar = ch;
return element;
}
toString() {
let output = "";
for (let y = 0; y < this.grid.height; y++) {
for (let x = 0; x < this.grid.width; x++) {
let element = this.grid.get(new Vector(x, y));
output += charFromElement(element);
}
output += "\n";
}
return output;
}
turn () {
let acted = [];
this.grid.forEach(function(critter, vector) {
if (critter.act && acted.indexOf(critter) == -1) {
acted.push(critter);
this.letAct(critter, vector);
}
}, this);
}
letAct(critter, vector) {
let action = critter.act(new View(this, vector));
if (action && action.type == "move") {
let dest = this.checkDestination(action, vector);
if (dest && this.grid.get(dest) == null) {
this.grid.set(vector, null);
this.grid.set(dest, critter);
}
}
}
checkDestination(action, vector) {
if (directions.hasOwnProperty(action.direction)) {
let dest = vector.plus(directions[action.direction]);
if (this.grid.isInside(dest))
return dest;
}
return undefined;
}
};
function charFromElement(element) {
if (element == null)
return " ";
else
return element.originChar;
}
function Wall() {};
exports.BouncingCritter=BouncingCritter;
exports.Grid=Grid;
exports.Wall=Wall;
exports.World=World;
exports.Vector=Vector;
exports.View=View;

122
assignments/A4/moarlife.js Normal file
View File

@ -0,0 +1,122 @@
let life=require("./life.js");
let View=life.View;
let actionTypes = Object.create(null);
actionTypes.grow = function(critter) {
critter.energy += 0.5;
return true;
};
actionTypes.move = function(critter, vector, action) {
let dest = this.checkDestination(action, vector);
if (dest == null ||
critter.energy <= 1 ||
this.grid.get(dest) != null)
return false;
critter.energy -= 1;
this.grid.set(vector, null);
this.grid.set(dest, critter);
return true;
};
actionTypes.eat = function(critter, vector, action) {
let dest = this.checkDestination(action, vector);
let atDest = dest != null && this.grid.get(dest);
if (!atDest || atDest.energy == null)
return false;
critter.energy += atDest.energy;
this.grid.set(dest, null);
return true;
};
actionTypes.reproduce = function(critter, vector, action) {
let baby = life.World.elementFromChar(this.legend,
critter.originChar);
let dest = this.checkDestination(action, vector);
if (dest == null ||
critter.energy <= 2 * baby.energy ||
this.grid.get(dest) != null)
return false;
critter.energy -= 2 * baby.energy;
this.grid.set(dest, baby);
return true;
};
actionTypes.die = function(critter, action) {
}
class LifelikeWorld extends life.World {
constructor(map,legend){
super(map,legend);
}
letAct(critter, vector) {
let action = critter.act(new View(this, vector));
let handled = action &&
action.type in actionTypes &&
actionTypes[action.type].call(this, critter,
vector, action);
if (!handled) {
critter.energy -= 0.2;
if (critter.energy <= 0)
this.grid.set(vector, null);
}
};
}
class Plant {
constructor() {
this.energy = 3 + Math.random() * 4;
}
act(view) {
if (this.energy > 15) {
let space = view.find(" ");
if (space)
return {type: "reproduce", direction: space};
}
if (this.energy < 20)
return {type: "grow"};
};
}
class PlantEater{
constructor () {
this.energy = 20;
}
act(view) {
let space = view.find(" ");
if (this.energy > 60 && space)
return {type: "reproduce", direction: space};
let plant = view.find("*");
if (plant)
return {type: "eat", direction: plant};
if (space)
return {type: "move", direction: space};
};
}
class ExplodingBunnyRabbit extends PlantEater {
constructor () {
super()
}
act(view) {
super.act(view);
if(this.energy > 55) {
if (Math.random() < 0.25) {
return {type: "die"}
}
}
}
}
exports.LifelikeWorld=LifelikeWorld;
exports.BouncingCritter=life.BouncingCritter;
exports.Wall=life.Wall;
exports.PlantEater = PlantEater;
exports.Plant = Plant;
exports.actionTypes = actionTypes;

View File

@ -0,0 +1,136 @@
let life=require ("../life.js");
let plan = ["############################",
"# # # o ##",
"# #",
"# ##### #",
"## # # ## #",
"### ## # #",
"# ### # #",
"# #### #",
"# ## o #",
"# o # o ### #",
"# # #",
"############################"];
let Vector = life.Vector;
describe("Grid",
function() {
it("initially undefined",
function() {
let grid = new life.Grid(5, 5);
expect(grid.get(new life.Vector(1, 1))).toBe(undefined);
});
it("setting a value",
function() {
let grid = new life.Grid(5, 5);
grid.set(new Vector(1, 1), "X");
expect(grid.get(new Vector(1, 1))).toEqual("X");
});
it("forEach",
function() {
let grid = new life.Grid(5, 5);
let test = {grid: grid, sum: 0,
method: function () {
this.grid.forEach(function() { this.sum++; }, this);
}
};
test.grid.set(new Vector(2,3), "#");
test.grid.set(new Vector(3,4), "#");
test.method();
expect(test.sum).toBe(2);
});
});
describe("BouncingCritter",
function() {
let bob = null;
beforeEach(function () {
spyOn(Math, 'random').and.returnValue(0.5);
bob=new life.BouncingCritter();
});
it("constructor",
function() {
expect('direction' in bob).toBe(true);
expect(bob.direction).toBe('s');
});
it("act, clear path",
function () {
let clear = {look: function () {return " ";}};
expect(bob.act(clear)).toEqual({type: "move", direction: "s"});
});
it("act, unclear path",
function () {
let unclear = {look: function () {return "#";}, find: function () { return "n";}};
expect(bob.act(unclear)).toEqual({type: "move", direction: "n"});
});
});
describe("World",
function () {
it("roundtrip",
function() {
let world = new life.World(plan, {"#": life.Wall, "o": life.BouncingCritter});
let rows = world.toString().split("\n");
// drop blank row
rows.pop();
expect(rows).toEqual(plan);
});
it("turn",
function () {
let world = new life.World(plan, {"#": life.Wall, "o": life.BouncingCritter});
let count=0;
spyOn(world, 'letAct').and.callFake(function(critter,vector) {count++;});
world.turn();
expect(count).toBe(4);
});
it("checkDestination",
function () {
let world = new life.World(plan, {"#": life.Wall, "o": life.BouncingCritter});
expect(world.checkDestination({direction: 's'},
new life.Vector(19,1))).toEqual(new life.Vector(19,2));
expect(world.checkDestination({direction: 'n'},
new life.Vector(0,0))).toEqual(undefined);
});
it("letAct",
function () {
let world = new life.World(plan, {"#": life.Wall, "o": life.BouncingCritter});
let src=new life.Vector(19,1);
let dest=new life.Vector(19,2);
let bob=world.grid.get(src);
spyOn(bob,'act').and.returnValue({type: 'move', direction: 's'});
world.letAct(bob, src);
expect(world.grid.get(dest)).toEqual(bob);
});
});
describe("View",
function () {
let world = new life.World(plan, {"#": life.Wall, "o": life.BouncingCritter});
let View=life.View;
let position=new Vector(15,9);
it("constructor",
function () {
let view=new View(world, position);
expect(view.vector).toEqual(position);
});
it("look",
function () {
let view=new View(world, position);
expect(view.look("s")).toBe(" ");
});
it("findAll",
function () {
let view=new View(world, position);
let directionNames = [ 'e', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w' ];
expect(view.findAll(" ").sort()).toEqual(directionNames);
});
it("find",
function () {
let view=new View(world, position);
spyOn(Math, 'random').and.returnValue(0.5);
expect(view.find(" ")).toBe('s');
});
});

View File

@ -0,0 +1,103 @@
let life=require ("../moarlife.js");
let plan= ["############################",
"##### ######",
"## *** **##",
"# *##** ** O *##",
"# *** O ##** *#",
"# O ##*** #",
"# ##** #",
"# O #* #",
"#* #** O #",
"#*** ##** O **#",
"##**** ###*** *###",
"############################"];
describe("World",
function () {
let valley = new life.LifelikeWorld(plan,
{"#": life.Wall,
"O": life.PlantEater,
"*": life.Plant});
it("roundtrip",
function() {
let rows = valley.toString().split("\n");
// drop blank row
rows.pop();
expect(rows).toEqual(plan);
});
});
describe("actionTypes",
function () {
it("grow",
function () {
let critter = new life.Plant();
let energy = critter.energy;
life.actionTypes.grow(critter);
expect(critter.energy).toBeGreaterThan(energy);
});
})
describe("PlantEater",
function () {
it("constructor",
function () {
let pe = new life.PlantEater();
expect('energy' in pe).toBe(true);
expect(pe.energy).toBe(20);
});
it("act, reproduce",
function () {
let pe = new life.PlantEater();
pe.energy = 65
expect(pe.act({find: function (ch) { if (ch === " ") return "n"; } })).toEqual({ type: "reproduce", direction: "n" });
});
it("act, eat",
function () {
let pe = new life.PlantEater();
pe.energy = 20
expect(pe.act({find: function (ch ) { if (ch === "*") return "n"; } })).toEqual({ type: "eat", direction: "n" });
});
it("act, move",
function () {
let pe = new life.PlantEater();
expect(pe.act({find: function (ch) { if (ch === " ") return "n"; } })).toEqual({ type: "move", direction: "n" });
});
});
describe("ExplodingBunnyRabbit",
function () {
it("constructor",
function () {
let pe = new life.ExplodingBunnyRabbit();
expect('energy' in pe).toBe(true);
expect(pe.energy).toBe(20);
});
it("act, reproduce",
function () {
let pe = new life.ExplodingBunnyRabbit();
pe.energy = 65
expect(pe.act({find: function (ch) { if (ch === " ") return "n"; } })).toEqual({ type: "reproduce", direction: "n" });
});
it("act, eat",
function () {
let pe = new life.ExplodingBunnyRabbit();
pe.energy = 20
expect(pe.act({find: function (ch ) { if (ch === "*") return "n"; } })).toEqual({ type: "eat", direction: "n" });
});
it("act, move",
function () {
let pe = new life.ExplodingBunnyRabbit();
expect(pe.act({find: function (ch) { if (ch === " ") return "n"; } })).toEqual({ type: "move", direction: "n" });
});
it("act, explode",
function () {
let pe = new life.ExplodingBunnyRabbit();
expect(pe.act({find: function (ch) { if (ch === "O") return "n"; } })).toEqual({ type: "explode", direction: "n" });
});
});

View File

@ -0,0 +1,13 @@
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.?(m)js"
],
"helpers": [
"helpers/**/*.?(m)js"
],
"env": {
"stopSpecOnExpectationFailure": false,
"random": true
}
}

28
assignments/A4/valley.js Normal file
View File

@ -0,0 +1,28 @@
let life=require("./moarlife.js");
let valley = new life.LifelikeWorld(
["############################",
"##### ######",
"## *** **##",
"# *##** ** O *##",
"# *** O ##** *#",
"# O ##*** #",
"# ##** #",
"# O #* #",
"#* #** O #",
"#*** ##** O **#",
"##**** ###*** *###",
"############################"],
{"#": life.Wall,
"O": life.PlantEater,
"*": life.Plant}
);
function loop () {
valley.turn();
console.log("\33c");
console.log(valley.toString());
setTimeout(function() { loop(); },250);
}
loop();

8
assignments/A5/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
assignments/A5/.idea/A5.iml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
assignments/A5/.idea/misc.xml generated Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (python-venv)" project-jdk-type="Python SDK" />
</project>

8
assignments/A5/.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/A5.iml" filepath="$PROJECT_DIR$/.idea/A5.iml" />
</modules>
</component>
</project>

6
assignments/A5/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

1000
assignments/A5/2014-1000.csv Normal file

File diff suppressed because one or more lines are too long

122
assignments/A5/readcsv.py Normal file
View File

@ -0,0 +1,122 @@
def read_csv(filename):
"""Read a CSV file, return list of rows"""
import csv
with open(filename, 'rt', newline='') as f:
reader = csv.reader(f, skipinitialspace=True)
return [row for row in reader]
def header_map(headers):
"""
Read a list, and convert it to a dictionary where the key is each element of the given list and the value is
its position in the list
:param headers: List
:return: Dict
"""
header_dict = dict()
i = 0
for header in headers:
header_dict[header] = i
i = i + 1
return header_dict
def select(table, search_items):
"""
Read the set in the second argument and search through the table in the first argument and return the
columns that match the query in the search items
:param table: List
:param search_items: List
:return: List
"""
header_numbers = header_map(table[0])
ret_list = list()
columns = list()
for item in search_items:
if type(item) is int: # Convert searched elements into strings if it is a number
columns.append(header_numbers[str(item)])
else:
columns.append(header_numbers[item])
columns.sort()
for item in table:
lst = list()
for number in columns:
lst.append(item[number])
ret_list.append(lst)
return ret_list
def row2dict(hmap, row):
"""
Convert a row in the second argument, given the headers in the first argument to a dictionary which uses the
headers as keys and the row data as values
:param hmap: Dictionary
:param row: List
:return: Dictionary
"""
ret_dict = dict()
for key in hmap:
ret_dict[key] = row[hmap[key]]
return ret_dict
def check_row(row, query):
"""
Check the row in the first argument passes a query in the second argument. The second argument is a formatted
tuple where the first element in the tuple is a column name, the second is an operation (==, <=, >=, AND,
OR) and the third element is a condition, numeric or string matching. (ex: is age == 34, is color == blue). AND
and OR are special in that you can pass in recursive tuples (left and right argument are tuples) that will also
be evaluated recursively
:param row: List
:param query: Tuple
:return: Boolean
"""
def perform_operation(op, var, cond):
if type(var) is str:
if var.isnumeric():
var = int(var)
if type(cond) is str:
if cond.isnumeric():
cond = int(cond)
if op == '==':
return var == cond
elif op == '>=':
return var >= cond
elif op == '<=':
return var <= cond
elif op == 'OR':
return perform_operation(var[1], row[var[0]], var[2]) or perform_operation(cond[1], row[cond[0]], cond[2])
elif op == 'AND':
return perform_operation(var[1], row[var[0]], var[2]) and perform_operation(cond[1], row[cond[0]], cond[2])
else:
return False
if type(query[0]) and type(query[2]) is tuple:
return perform_operation(query[1], query[0], query[2])
else:
stringify = str(query[2])
return perform_operation(query[1], row[str(query[0])], stringify)
def filter_table(table, query):
"""
This function takes a table of csv values, and performs the query to filter out the rows that do not match the query
:param table: List
:param query: Tuple
:return: List
"""
header_row = header_map(table[0])
data_rows = table[1:]
result = list()
result.append(table[0])
for row in data_rows:
data_dict = row2dict(header_row, row)
if check_row(data_dict, query):
result.append(row)
return result

4
assignments/A5/test1.csv Normal file
View File

@ -0,0 +1,4 @@
name, age, eye colour
Bob, 5, blue
Mary, 27, brown
Vij, 54, green
1 name age eye colour
2 Bob 5 blue
3 Mary 27 brown
4 Vij 54 green

3
assignments/A5/test2.csv Normal file
View File

@ -0,0 +1,3 @@
name, 100
teddy, 500
lovely, 1000
1 name 100
2 teddy 500
3 lovely 1000

View File

@ -0,0 +1,114 @@
from readcsv import read_csv
from readcsv import header_map
from readcsv import select
from readcsv import row2dict
from readcsv import check_row
from readcsv import filter_table
table = read_csv('test1.csv')
sampledata = read_csv('2014-1000.csv')
def test_read_csv():
assert read_csv('test1.csv') == [['name', 'age', 'eye colour'],
['Bob', '5', 'blue'],
['Mary', '27', 'brown'],
['Vij', '54', 'green']]
def test_header_map_1():
hmap = header_map(table[0])
assert hmap == {'name': 0, 'age': 1, 'eye colour': 2}
def test_select_1():
assert select(table, {'name', 'eye colour'}) == [['name', 'eye colour'],
['Bob', 'blue'],
['Mary', 'brown'],
['Vij', 'green']]
def test_row2dict():
hmap = header_map(table[0])
assert row2dict(hmap, table[1]) == {'name': 'Bob', 'age': '5', 'eye colour': 'blue'}
def test_check_row():
row = {'name': 'Bob', 'age': '5', 'eye colour': 'blue'}
assert check_row(row, ('age', '==', 5))
assert not check_row(row, ('eye colour', '==', 5))
assert check_row(row, ('eye colour', '==', 'blue'))
assert check_row(row, ('age', '>=', 4))
assert check_row(row, ('age', '<=', 1000))
def test_check_row_logical():
row = {'name': 'Bob', 'age': '5', 'eye colour': 'blue'}
assert check_row(row, (('age', '==', 5), 'OR', ('eye colour', '==', 5)))
assert not check_row(row, (('age', '==', 5), 'AND', ('eye colour', '==', 5)))
def test_filter_table1():
assert filter_table(table, ('age', '>=', 0)) == [['name', 'age', 'eye colour'],
['Bob', '5', 'blue'],
['Mary', '27', 'brown'],
['Vij', '54', 'green']]
assert filter_table(table, ('age', '<=', 27)) == [['name', 'age', 'eye colour'],
['Bob', '5', 'blue'],
['Mary', '27', 'brown']]
assert filter_table(table, ('eye colour', '==', 'brown')) == [['name', 'age', 'eye colour'],
['Mary', '27', 'brown']]
assert filter_table(table, ('name', '==', 'Vij')) == [['name', 'age', 'eye colour'],
['Vij', '54', 'green']]
def test_filter_table2():
assert filter_table(table, (('age', '>=', 0), 'AND', ('age', '>=', '27'))) == [['name', 'age', 'eye colour'],
['Mary', '27', 'brown'],
['Vij', '54', 'green']]
assert filter_table(table, (('age', '<=', 27), 'AND', ('age', '>=', '27'))) == [['name', 'age', 'eye colour'],
['Mary', '27', 'brown']]
assert filter_table(table, (('eye colour', '==', 'brown'),
'OR',
('name', '==', 'Vij'))) == [['name', 'age', 'eye colour'],
['Mary', '27', 'brown'],
['Vij', '54', 'green']]
# Student Tests
table2 = read_csv('test2.csv')
hmap2 = header_map(table2[0])
def test_header_map2():
assert header_map(table2[0]) == {"name": 0, "100": 1}
def test_select2():
assert select(table2, [100]) == [["100"], ["500"], ["1000"]]
assert select(table2, ["name"]) == [["name"], ["teddy"], ["lovely"]]
assert select(table2, ["name", 100]) == [["name", "100"], ["teddy", "500"], ["lovely", "1000"]]
def test_row2dict2():
assert row2dict(hmap2, table2[1]) == {"name": "teddy", "100": "500"}
assert row2dict(hmap2, table2[2]) == {"name": "lovely", "100": "1000"}
def test_check_row_2():
row = {'name': 'Bob', 'age': '5', 'eye colour': 'blue'}
assert not check_row(row, ('age', '===', 5))
def test_check_row_3():
row = row2dict(hmap2, table2[1])
assert check_row(row, ("100", "==", 500))
def test_filter_table3():
assert filter_table(table2, ("100", ">=", 100)) == [["name", "100"], ["teddy", "500"], ["lovely", "1000"]]

File diff suppressed because it is too large Load Diff

32
assignments/A6/classify.m Normal file
View File

@ -0,0 +1,32 @@
iris = csvread("iris.csv");
[training, testing] = randomsplit(iris, 2/3)
p = 2
cells = p^(columns(iris)-1)+1
minmax = ranges(iris);
classes = minmax(2,1) - minmax(1,1) + 1;
votes = zeros(cells,classes);
for i=1:rows(training)
label = training(i,1);
hashval = hash(training(i,:), minmax, p);
votes(hashval,label) += 1;
endfor
classification = tally(votes)
correct = 0
for i=1:rows(testing);
hashval = hash(testing(i,:), minmax, p);
class=classification(hashval);
label = testing(i,1);
if label == class
correct += 1;
endif
endfor
display(correct/rows(testing))

151
assignments/A6/iris.csv Normal file
View File

@ -0,0 +1,151 @@
1,5.1,3.5,1.4,0.2
1,4.9,3.0,1.4,0.2
1,4.7,3.2,1.3,0.2
1,4.6,3.1,1.5,0.2
1,5.0,3.6,1.4,0.2
1,5.4,3.9,1.7,0.4
1,4.6,3.4,1.4,0.3
1,5.0,3.4,1.5,0.2
1,4.4,2.9,1.4,0.2
1,4.9,3.1,1.5,0.1
1,5.4,3.7,1.5,0.2
1,4.8,3.4,1.6,0.2
1,4.8,3.0,1.4,0.1
1,4.3,3.0,1.1,0.1
1,5.8,4.0,1.2,0.2
1,5.7,4.4,1.5,0.4
1,5.4,3.9,1.3,0.4
1,5.1,3.5,1.4,0.3
1,5.7,3.8,1.7,0.3
1,5.1,3.8,1.5,0.3
1,5.4,3.4,1.7,0.2
1,5.1,3.7,1.5,0.4
1,4.6,3.6,1.0,0.2
1,5.1,3.3,1.7,0.5
1,4.8,3.4,1.9,0.2
1,5.0,3.0,1.6,0.2
1,5.0,3.4,1.6,0.4
1,5.2,3.5,1.5,0.2
1,5.2,3.4,1.4,0.2
1,4.7,3.2,1.6,0.2
1,4.8,3.1,1.6,0.2
1,5.4,3.4,1.5,0.4
1,5.2,4.1,1.5,0.1
1,5.5,4.2,1.4,0.2
1,4.9,3.1,1.5,0.1
1,5.0,3.2,1.2,0.2
1,5.5,3.5,1.3,0.2
1,4.9,3.1,1.5,0.1
1,4.4,3.0,1.3,0.2
1,5.1,3.4,1.5,0.2
1,5.0,3.5,1.3,0.3
1,4.5,2.3,1.3,0.3
1,4.4,3.2,1.3,0.2
1,5.0,3.5,1.6,0.6
1,5.1,3.8,1.9,0.4
1,4.8,3.0,1.4,0.3
1,5.1,3.8,1.6,0.2
1,4.6,3.2,1.4,0.2
1,5.3,3.7,1.5,0.2
1,5.0,3.3,1.4,0.2
2,7.0,3.2,4.7,1.4
2,6.4,3.2,4.5,1.5
2,6.9,3.1,4.9,1.5
2,5.5,2.3,4.0,1.3
2,6.5,2.8,4.6,1.5
2,5.7,2.8,4.5,1.3
2,6.3,3.3,4.7,1.6
2,4.9,2.4,3.3,1.0
2,6.6,2.9,4.6,1.3
2,5.2,2.7,3.9,1.4
2,5.0,2.0,3.5,1.0
2,5.9,3.0,4.2,1.5
2,6.0,2.2,4.0,1.0
2,6.1,2.9,4.7,1.4
2,5.6,2.9,3.6,1.3
2,6.7,3.1,4.4,1.4
2,5.6,3.0,4.5,1.5
2,5.8,2.7,4.1,1.0
2,6.2,2.2,4.5,1.5
2,5.6,2.5,3.9,1.1
2,5.9,3.2,4.8,1.8
2,6.1,2.8,4.0,1.3
2,6.3,2.5,4.9,1.5
2,6.1,2.8,4.7,1.2
2,6.4,2.9,4.3,1.3
2,6.6,3.0,4.4,1.4
2,6.8,2.8,4.8,1.4
2,6.7,3.0,5.0,1.7
2,6.0,2.9,4.5,1.5
2,5.7,2.6,3.5,1.0
2,5.5,2.4,3.8,1.1
2,5.5,2.4,3.7,1.0
2,5.8,2.7,3.9,1.2
2,6.0,2.7,5.1,1.6
2,5.4,3.0,4.5,1.5
2,6.0,3.4,4.5,1.6
2,6.7,3.1,4.7,1.5
2,6.3,2.3,4.4,1.3
2,5.6,3.0,4.1,1.3
2,5.5,2.5,4.0,1.3
2,5.5,2.6,4.4,1.2
2,6.1,3.0,4.6,1.4
2,5.8,2.6,4.0,1.2
2,5.0,2.3,3.3,1.0
2,5.6,2.7,4.2,1.3
2,5.7,3.0,4.2,1.2
2,5.7,2.9,4.2,1.3
2,6.2,2.9,4.3,1.3
2,5.1,2.5,3.0,1.1
2,5.7,2.8,4.1,1.3
3,6.3,3.3,6.0,2.5
3,5.8,2.7,5.1,1.9
3,7.1,3.0,5.9,2.1
3,6.3,2.9,5.6,1.8
3,6.5,3.0,5.8,2.2
3,7.6,3.0,6.6,2.1
3,4.9,2.5,4.5,1.7
3,7.3,2.9,6.3,1.8
3,6.7,2.5,5.8,1.8
3,7.2,3.6,6.1,2.5
3,6.5,3.2,5.1,2.0
3,6.4,2.7,5.3,1.9
3,6.8,3.0,5.5,2.1
3,5.7,2.5,5.0,2.0
3,5.8,2.8,5.1,2.4
3,6.4,3.2,5.3,2.3
3,6.5,3.0,5.5,1.8
3,7.7,3.8,6.7,2.2
3,7.7,2.6,6.9,2.3
3,6.0,2.2,5.0,1.5
3,6.9,3.2,5.7,2.3
3,5.6,2.8,4.9,2.0
3,7.7,2.8,6.7,2.0
3,6.3,2.7,4.9,1.8
3,6.7,3.3,5.7,2.1
3,7.2,3.2,6.0,1.8
3,6.2,2.8,4.8,1.8
3,6.1,3.0,4.9,1.8
3,6.4,2.8,5.6,2.1
3,7.2,3.0,5.8,1.6
3,7.4,2.8,6.1,1.9
3,7.9,3.8,6.4,2.0
3,6.4,2.8,5.6,2.2
3,6.3,2.8,5.1,1.5
3,6.1,2.6,5.6,1.4
3,7.7,3.0,6.1,2.3
3,6.3,3.4,5.6,2.4
3,6.4,3.1,5.5,1.8
3,6.0,3.0,4.8,1.8
3,6.9,3.1,5.4,2.1
3,6.7,3.1,5.6,2.4
3,6.9,3.1,5.1,2.3
3,5.8,2.7,5.1,1.9
3,6.8,3.2,5.9,2.3
3,6.7,3.3,5.7,2.5
3,6.7,3.0,5.2,2.3
3,6.3,2.5,5.0,1.9
3,6.5,3.0,5.2,2.0
3,6.2,3.4,5.4,2.3
3,5.9,3.0,5.1,1.8
1 1 5.1 3.5 1.4 0.2
2 1 4.9 3.0 1.4 0.2
3 1 4.7 3.2 1.3 0.2
4 1 4.6 3.1 1.5 0.2
5 1 5.0 3.6 1.4 0.2
6 1 5.4 3.9 1.7 0.4
7 1 4.6 3.4 1.4 0.3
8 1 5.0 3.4 1.5 0.2
9 1 4.4 2.9 1.4 0.2
10 1 4.9 3.1 1.5 0.1
11 1 5.4 3.7 1.5 0.2
12 1 4.8 3.4 1.6 0.2
13 1 4.8 3.0 1.4 0.1
14 1 4.3 3.0 1.1 0.1
15 1 5.8 4.0 1.2 0.2
16 1 5.7 4.4 1.5 0.4
17 1 5.4 3.9 1.3 0.4
18 1 5.1 3.5 1.4 0.3
19 1 5.7 3.8 1.7 0.3
20 1 5.1 3.8 1.5 0.3
21 1 5.4 3.4 1.7 0.2
22 1 5.1 3.7 1.5 0.4
23 1 4.6 3.6 1.0 0.2
24 1 5.1 3.3 1.7 0.5
25 1 4.8 3.4 1.9 0.2
26 1 5.0 3.0 1.6 0.2
27 1 5.0 3.4 1.6 0.4
28 1 5.2 3.5 1.5 0.2
29 1 5.2 3.4 1.4 0.2
30 1 4.7 3.2 1.6 0.2
31 1 4.8 3.1 1.6 0.2
32 1 5.4 3.4 1.5 0.4
33 1 5.2 4.1 1.5 0.1
34 1 5.5 4.2 1.4 0.2
35 1 4.9 3.1 1.5 0.1
36 1 5.0 3.2 1.2 0.2
37 1 5.5 3.5 1.3 0.2
38 1 4.9 3.1 1.5 0.1
39 1 4.4 3.0 1.3 0.2
40 1 5.1 3.4 1.5 0.2
41 1 5.0 3.5 1.3 0.3
42 1 4.5 2.3 1.3 0.3
43 1 4.4 3.2 1.3 0.2
44 1 5.0 3.5 1.6 0.6
45 1 5.1 3.8 1.9 0.4
46 1 4.8 3.0 1.4 0.3
47 1 5.1 3.8 1.6 0.2
48 1 4.6 3.2 1.4 0.2
49 1 5.3 3.7 1.5 0.2
50 1 5.0 3.3 1.4 0.2
51 2 7.0 3.2 4.7 1.4
52 2 6.4 3.2 4.5 1.5
53 2 6.9 3.1 4.9 1.5
54 2 5.5 2.3 4.0 1.3
55 2 6.5 2.8 4.6 1.5
56 2 5.7 2.8 4.5 1.3
57 2 6.3 3.3 4.7 1.6
58 2 4.9 2.4 3.3 1.0
59 2 6.6 2.9 4.6 1.3
60 2 5.2 2.7 3.9 1.4
61 2 5.0 2.0 3.5 1.0
62 2 5.9 3.0 4.2 1.5
63 2 6.0 2.2 4.0 1.0
64 2 6.1 2.9 4.7 1.4
65 2 5.6 2.9 3.6 1.3
66 2 6.7 3.1 4.4 1.4
67 2 5.6 3.0 4.5 1.5
68 2 5.8 2.7 4.1 1.0
69 2 6.2 2.2 4.5 1.5
70 2 5.6 2.5 3.9 1.1
71 2 5.9 3.2 4.8 1.8
72 2 6.1 2.8 4.0 1.3
73 2 6.3 2.5 4.9 1.5
74 2 6.1 2.8 4.7 1.2
75 2 6.4 2.9 4.3 1.3
76 2 6.6 3.0 4.4 1.4
77 2 6.8 2.8 4.8 1.4
78 2 6.7 3.0 5.0 1.7
79 2 6.0 2.9 4.5 1.5
80 2 5.7 2.6 3.5 1.0
81 2 5.5 2.4 3.8 1.1
82 2 5.5 2.4 3.7 1.0
83 2 5.8 2.7 3.9 1.2
84 2 6.0 2.7 5.1 1.6
85 2 5.4 3.0 4.5 1.5
86 2 6.0 3.4 4.5 1.6
87 2 6.7 3.1 4.7 1.5
88 2 6.3 2.3 4.4 1.3
89 2 5.6 3.0 4.1 1.3
90 2 5.5 2.5 4.0 1.3
91 2 5.5 2.6 4.4 1.2
92 2 6.1 3.0 4.6 1.4
93 2 5.8 2.6 4.0 1.2
94 2 5.0 2.3 3.3 1.0
95 2 5.6 2.7 4.2 1.3
96 2 5.7 3.0 4.2 1.2
97 2 5.7 2.9 4.2 1.3
98 2 6.2 2.9 4.3 1.3
99 2 5.1 2.5 3.0 1.1
100 2 5.7 2.8 4.1 1.3
101 3 6.3 3.3 6.0 2.5
102 3 5.8 2.7 5.1 1.9
103 3 7.1 3.0 5.9 2.1
104 3 6.3 2.9 5.6 1.8
105 3 6.5 3.0 5.8 2.2
106 3 7.6 3.0 6.6 2.1
107 3 4.9 2.5 4.5 1.7
108 3 7.3 2.9 6.3 1.8
109 3 6.7 2.5 5.8 1.8
110 3 7.2 3.6 6.1 2.5
111 3 6.5 3.2 5.1 2.0
112 3 6.4 2.7 5.3 1.9
113 3 6.8 3.0 5.5 2.1
114 3 5.7 2.5 5.0 2.0
115 3 5.8 2.8 5.1 2.4
116 3 6.4 3.2 5.3 2.3
117 3 6.5 3.0 5.5 1.8
118 3 7.7 3.8 6.7 2.2
119 3 7.7 2.6 6.9 2.3
120 3 6.0 2.2 5.0 1.5
121 3 6.9 3.2 5.7 2.3
122 3 5.6 2.8 4.9 2.0
123 3 7.7 2.8 6.7 2.0
124 3 6.3 2.7 4.9 1.8
125 3 6.7 3.3 5.7 2.1
126 3 7.2 3.2 6.0 1.8
127 3 6.2 2.8 4.8 1.8
128 3 6.1 3.0 4.9 1.8
129 3 6.4 2.8 5.6 2.1
130 3 7.2 3.0 5.8 1.6
131 3 7.4 2.8 6.1 1.9
132 3 7.9 3.8 6.4 2.0
133 3 6.4 2.8 5.6 2.2
134 3 6.3 2.8 5.1 1.5
135 3 6.1 2.6 5.6 1.4
136 3 7.7 3.0 6.1 2.3
137 3 6.3 3.4 5.6 2.4
138 3 6.4 3.1 5.5 1.8
139 3 6.0 3.0 4.8 1.8
140 3 6.9 3.1 5.4 2.1
141 3 6.7 3.1 5.6 2.4
142 3 6.9 3.1 5.1 2.3
143 3 5.8 2.7 5.1 1.9
144 3 6.8 3.2 5.9 2.3
145 3 6.7 3.3 5.7 2.5
146 3 6.7 3.0 5.2 2.3
147 3 6.3 2.5 5.0 1.9
148 3 6.5 3.0 5.2 2.0
149 3 6.2 3.4 5.4 2.3
150 3 5.9 3.0 5.1 1.8

View File

@ -0,0 +1 @@
# Created by Octave 7.3.0, Wed Dec 07 23:43:44 2022 GMT <unknown@Isaac-DesktopPC>

View File

@ -1,6 +1,6 @@
Title: Lab One
Date: 2022-09-07T08:30:00
Tags: cs2163, lab, frog, git
Tags: cs2613, lab, frog, git
In this lab we learned about how to use frog and git, and how to interact with the university git remote.
<!-- more -->
## Frog

View File

@ -1,6 +1,6 @@
Title: Lab Two
Date: 2022-09-12T08:30:00
Tags: cs2163, lab, git, racket
Tags: cs2613, lab, git, racket
In this lab we learned more about git, and using racket to draw shapes
<!-- more -->
## Additional Git Information

View File

@ -1,6 +1,6 @@
Title: Lab Three
Date: 2022-09-14T08:30:00
Tags: cs2163, lab, git, racket
Tags: cs2613, lab, git, racket
In this lab I learned about Racket recursion techniques.
<!-- more -->

View File

@ -1,6 +1,6 @@
Title: Lab Six
Date: 2022-09-28T08:30:00
Tags: cs2163, lab, racket, hash-table, json
Tags: cs2613, lab, racket, hash-table, json
In this lab I learned about using hash sets and json parsing in racket.
<!-- more -->

View File

@ -1,6 +1,6 @@
Title: Lab Seven
Date: 2022-10-03T08:30:00
Tags: cs2163, lab, racket, macros
Tags: cs2613, lab, racket, macros
In this lab I learned about macros
<!-- more -->

View File

@ -1,6 +1,6 @@
Title: Lab Eight
Date: 2022-10-05T08:30:00
Tags: cs2163, lab, javascript, type-coercion, functions, modules
Tags: cs2613, lab, javascript, type-coercion, functions, modules
In this lab I learned the javascript basics
<!-- more -->

View File

@ -0,0 +1,21 @@
Title: Lab Nine
Date: 2022-10-12T08:30:00
Tags: cs2613, lab, javascript, tests, jasmine, nyc, test-coverage, debugging, objects, json
In this lab, I learned about how to use tests in javascript and how to view my test coverage
<!-- more -->
## Testing in javascript with Jasmine
With jasmine, tests are stored in *.spec.js files, and each "spec" is a way to give a test a name and designate it a purpose. Specs can be grouped in describe blocks that are often testing one function. Tests follow a syntax of passing in a function result into an `expect` function, which then tests it with the `toBe` function and if the results are the same the test passes.
## Viewing test coverage with nyc
With nyc you can view the test coverage of a jasmine test suite, and it will show you what percent of functions, and lines are uncovered, as well as which lines are not covered with a test.
## Debugging javascript
You can debug a javascript file with the `node inspect "filename.js"` command, and you can then inspect it from the command line or use an interactive debugger in a chrome based browser.
## Objects in javascript
Objects in javascript are similar to mutable hash tables, and you can see this as an object function is just the "key" to a function inside the object.
## Json parsing
In javascript you can parse a json file and parse it directly into an object. You can then perform any actions on this object. Assuming you are aware of what the object should contain you can then perform objects actually fitting of the object (like searching for a name in a json array of people).

View File

@ -0,0 +1,17 @@
Title: Lab Ten
Date: 2022-10-17T08:30:00
Tags: cs2613, lab, javascript, methods, tests, classes, prototypes, arrays, recursion
In this lab, I learned about classes, prototypes, arrays and how to use recursion in javascript
<!-- more -->
## Prototypes
In javascript, there is a notion of a prototype, which is a way of making objects by calling the `Object.create()` function on a "prototype" of an object, which can be viewed as a form of constructor
## Classes
In reality, classes should be made by the new keyword instead of calling `Object.create()`. In "recent" times there has been an addition to javascript which allows the use of the class keyword and a constructor keyword, which lets you define an object in a very similar way to Java. It should be noted that both the new and the class/constructor syntax is just the same prototype based system that has been made more convenient to read.
## Arrays
Arrays in javascript are implemented as objects, which is similar to java, but due to the type casting nature of javascript can lead to some unintuitive behavior. We were tasked to create a function that takes a range and creates an array filled with numbers from the start of the range to the end. This can be achieved with a simple for loop.
We were also tasked with creating function that sums the elements of a javascript array. This can also be done with a for loop, by looping over each element and adding it to a running total and returning the result. We were introduced to some higher order functions like reduce and forEach, which take functions as arguments and are similar to `foldl` in racket. We can use foreach as a way to skip the syntax of a normal for loop and simply operate once on each element of an array.
## Recursion
We were tasked with using recursion to recreate the `mult` and `add` methods from the first javascript lab, but with recursion. In my opinion it was much more readable than the sample racket solution, but there was an noticeable similarity in the syntax

View File

@ -0,0 +1,9 @@
Title: Lab Eleven
Date: 2022-10-19T08:30:00
Tags: cs2613, lab, javascript
Sample description
<!-- more -->
## Sample Body
Sample Body

View File

@ -0,0 +1,9 @@
Title: Lab Twelve
Date: 2022-10-24T08:30:00
Tags: cs2613, lab, javascript
Sample description
<!-- more -->
## Sample Body
Sample Body

View File

@ -0,0 +1,9 @@
Title: Lab Thirteen
Date: 2022-10-26T08:30:00
Tags: cs2613, lab, javascript
Sample description
<!-- more -->
## Sample Body
Sample Body

View File

@ -0,0 +1,21 @@
Title: Lab Fourteen
Date: 2022-10-31T08:30:00
Tags: cs2613, lab, python, pytest, exceptions, modules
In this lab I learned about python, pytest, python exceptions and modules.
<!-- more -->
## Pytest
Pytest is a python testing framework. It can show you code coverage with line counts. This is pretty similar to `nyc` from javascript. You run tests by just running `pytest` or `pytest -cov` for code coverage. Tests are defined by in a file having a function with an assert statement. Pytest will then scan your files for assert statements and run those functions to run tests. Testing in python is pretty simple, like with javascript. Racket testing is a little more abstract and confusing in my opinion.
## Modules
In python functions are exported by default, in contrast to javascript. You must guard your main method with the `if __name__ =='__main__'` text so it does not run when the module is imported. You can make tests to ensure you have documentation for your functions, by checking for the imports `__doc__` variable.
## Indentation
In python, blocks are defined by indentation (also known as whitespace). This sets the scope for a given statement. It can be a little confusing to tell if an if statement is in a for loop or not, but linters help.
## Exceptions
Python can raise errors when things go wrong, for example diving by zero. You can catch these exceptions and handle them if you desire.
## FizzBuzz missing case
You can fix the missing case just by adding another if statement above the other if statement for the fizz case.

View File

@ -6,7 +6,7 @@
<title>CS2163 Blog</title>
<meta name="description" content="CS2163 Blog">
<meta name="author" content="Isaac Shoebottom">
<meta name="keywords" content="recursion, lab, cs2613, git, frog, all, pattern-matching, scribble, cs2163, racket">
<meta name="keywords" content="json, recursion, objects, hash-table, tests, javascript, frog, pattern-matching, scribble, racket, cs2163, jasmine, macros, cs2613, lab, git, modules, all, functions, nyc, test-coverage, type-coercion, debugging">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="/favicon.ico">
<link rel="canonical" href="http://www.example.com/index.html">
@ -57,7 +57,7 @@
</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="/tags\cs2163.html">cs2163</a><a class="dropdown-item" href="/tags\cs2613.html">cs2613</a><a class="dropdown-item" href="/tags\frog.html">frog</a><a class="dropdown-item" href="/tags\git.html">git</a><a class="dropdown-item" href="/tags\lab.html">lab</a><a class="dropdown-item" href="/tags\pattern-matching.html">pattern-matching</a><a class="dropdown-item" href="/tags\racket.html">racket</a><a class="dropdown-item" href="/tags\recursion.html">recursion</a><a class="dropdown-item" href="/tags\scribble.html">scribble</a>
<a class="dropdown-item" href="/tags\cs2163.html">cs2163</a><a class="dropdown-item" href="/tags\cs2613.html">cs2613</a><a class="dropdown-item" href="/tags\debugging.html">debugging</a><a class="dropdown-item" href="/tags\frog.html">frog</a><a class="dropdown-item" href="/tags\functions.html">functions</a><a class="dropdown-item" href="/tags\git.html">git</a><a class="dropdown-item" href="/tags\hash-table.html">hash-table</a><a class="dropdown-item" href="/tags\jasmine.html">jasmine</a><a class="dropdown-item" href="/tags\javascript.html">javascript</a><a class="dropdown-item" href="/tags\json.html">json</a><a class="dropdown-item" href="/tags\lab.html">lab</a><a class="dropdown-item" href="/tags\macros.html">macros</a><a class="dropdown-item" href="/tags\modules.html">modules</a><a class="dropdown-item" href="/tags\nyc.html">nyc</a><a class="dropdown-item" href="/tags\objects.html">objects</a><a class="dropdown-item" href="/tags\pattern-matching.html">pattern-matching</a><a class="dropdown-item" href="/tags\racket.html">racket</a><a class="dropdown-item" href="/tags\recursion.html">recursion</a><a class="dropdown-item" href="/tags\scribble.html">scribble</a><a class="dropdown-item" href="/tags\test-coverage.html">test-coverage</a><a class="dropdown-item" href="/tags\tests.html">tests</a><a class="dropdown-item" href="/tags\type-coercion.html">type-coercion</a>
</div>
</li>
@ -90,6 +90,58 @@
<article>
<header>
<h2><a href='/2022\10\lab-nine.html'>Lab Nine</a></h2>
<p class='date-and-tags'>
<time datetime="2022-10-12" pubdate="true">2022-10-12</time> - <span class="tags"><a href="/tags\cs2163.html">cs2163</a>, <a href="/tags\lab.html">lab</a>, <a href="/tags\javascript.html">javascript</a>, <a href="/tags\tests.html">tests</a>, <a href="/tags\jasmine.html">jasmine</a>, <a href="/tags\nyc.html">nyc</a>, <a href="/tags\test-coverage.html">test-coverage</a>, <a href="/tags\debugging.html">debugging</a>, <a href="/tags\objects.html">objects</a>, <a href="/tags\json.html">json</a></span></p>
<p class='authors'>By: <span class="authors">Isaac Shoebottom</span></p>
</header>
<p>In this lab, I learned about how to use tests in javascript and how to view my test coverage</p>
<footer>
<a href='/2022\10\lab-nine.html'>&hellip; more &hellip;</a>
</footer>
</article>
<article>
<header>
<h2><a href='/2022\10\lab-eight.html'>Lab Eight</a></h2>
<p class='date-and-tags'>
<time datetime="2022-10-05" pubdate="true">2022-10-05</time> - <span class="tags"><a href="/tags\cs2163.html">cs2163</a>, <a href="/tags\lab.html">lab</a>, <a href="/tags\javascript.html">javascript</a>, <a href="/tags\type-coercion.html">type-coercion</a>, <a href="/tags\functions.html">functions</a>, <a href="/tags\modules.html">modules</a></span></p>
<p class='authors'>By: <span class="authors">Isaac Shoebottom</span></p>
</header>
<p>In this lab I learned the javascript basics</p>
<footer>
<a href='/2022\10\lab-eight.html'>&hellip; more &hellip;</a>
</footer>
</article>
<article>
<header>
<h2><a href='/2022\10\lab-seven.html'>Lab Seven</a></h2>
<p class='date-and-tags'>
<time datetime="2022-10-03" pubdate="true">2022-10-03</time> - <span class="tags"><a href="/tags\cs2163.html">cs2163</a>, <a href="/tags\lab.html">lab</a>, <a href="/tags\racket.html">racket</a>, <a href="/tags\macros.html">macros</a></span></p>
<p class='authors'>By: <span class="authors">Isaac Shoebottom</span></p>
</header>
<p>In this lab I learned about macros</p>
<footer>
<a href='/2022\10\lab-seven.html'>&hellip; more &hellip;</a>
</footer>
</article>
<article>
<header>
<h2><a href='/2022\09\lab-six.html'>Lab Six</a></h2>
<p class='date-and-tags'>
<time datetime="2022-09-28" pubdate="true">2022-09-28</time> - <span class="tags"><a href="/tags\cs2163.html">cs2163</a>, <a href="/tags\lab.html">lab</a>, <a href="/tags\racket.html">racket</a>, <a href="/tags\hash-table.html">hash-table</a>, <a href="/tags\json.html">json</a></span></p>
<p class='authors'>By: <span class="authors">Isaac Shoebottom</span></p>
</header>
<p>In this lab I learned about using hash sets and json parsing in racket.</p>
<footer>
<a href='/2022\09\lab-six.html'>&hellip; more &hellip;</a>
</footer>
</article>
<article>
<header>
<h2><a href='/2022\09\lab-five.html'>Lab Five</a></h2>
<p class='date-and-tags'>

View File

@ -1,7 +1,11 @@
http://www.example.com/2022\09\lab-three.html
http://www.example.com/2022\10\lab-eight.html
http://www.example.com/2022\09\lab-two.html
http://www.example.com/2022\09\lab-five.html
http://www.example.com/2022\09\lab-four-scribble-demo.html
http://www.example.com/2022\09\lab-four.html
http://www.example.com/2022\09\lab-six.html
http://www.example.com/2022\09\lab-one.html
http://www.example.com/2022\10\lab-nine.html
http://www.example.com/2022\09\lab-four-scribble-demo.html
http://www.example.com/2022\09\lab-five.html
http://www.example.com/2022\09\lab-four.html
http://www.example.com/2022\10\lab-seven.html
http://www.example.com/About.html

12
labs/L09/ancestry.js Normal file
View File

@ -0,0 +1,12 @@
let read_json_file = require("./read_json_file.js").read_json_file;
let data = null;
function by_name(name) {
if (data === null)
data = read_json_file("./ancestry.json");
// simple linear scan
result = data.find(person => person.name === name) // find the result of the first person with the name
return (result === undefined) ? null : result; // if the result is undefined, return null, otherwise return the result (since tests expect null not undefined)
}
exports.by_name = by_name;

41
labs/L09/ancestry.json Normal file
View File

@ -0,0 +1,41 @@
[
{"name": "Carolus Haverbeke", "sex": "m", "born": 1832, "died": 1905, "father": "Carel Haverbeke", "mother": "Maria van Brussel"},
{"name": "Emma de Milliano", "sex": "f", "born": 1876, "died": 1956, "father": "Petrus de Milliano", "mother": "Sophia van Damme"},
{"name": "Maria de Rycke", "sex": "f", "born": 1683, "died": 1724, "father": "Frederik de Rycke", "mother": "Laurentia van Vlaenderen"},
{"name": "Jan van Brussel", "sex": "m", "born": 1714, "died": 1748, "father": "Jacobus van Brussel", "mother": "Joanna van Rooten"},
{"name": "Philibert Haverbeke", "sex": "m", "born": 1907, "died": 1997, "father": "Emile Haverbeke", "mother": "Emma de Milliano"},
{"name": "Jan Frans van Brussel", "sex": "m", "born": 1761, "died": 1833, "father": "Jacobus Bernardus van Brussel", "mother":null},
{"name": "Pauwels van Haverbeke", "sex": "m", "born": 1535, "died": 1582, "father": "N. van Haverbeke", "mother":null},
{"name": "Clara Aernoudts", "sex": "f", "born": 1918, "died": 2012, "father": "Henry Aernoudts", "mother": "Sidonie Coene"},
{"name": "Emile Haverbeke", "sex": "m", "born": 1877, "died": 1968, "father": "Carolus Haverbeke", "mother": "Maria Sturm"},
{"name": "Lieven de Causmaecker", "sex": "m", "born": 1696, "died": 1724, "father": "Carel de Causmaecker", "mother": "Joanna Claes"},
{"name": "Pieter Haverbeke", "sex": "m", "born": 1602, "died": 1642, "father": "Lieven van Haverbeke", "mother":null},
{"name": "Livina Haverbeke", "sex": "f", "born": 1692, "died": 1743, "father": "Daniel Haverbeke", "mother": "Joanna de Pape"},
{"name": "Pieter Bernard Haverbeke", "sex": "m", "born": 1695, "died": 1762, "father": "Willem Haverbeke", "mother": "Petronella Wauters"},
{"name": "Lieven van Haverbeke", "sex": "m", "born": 1570, "died": 1636, "father": "Pauwels van Haverbeke", "mother": "Lievijne Jans"},
{"name": "Joanna de Causmaecker", "sex": "f", "born": 1762, "died": 1807, "father": "Bernardus de Causmaecker", "mother":null},
{"name": "Willem Haverbeke", "sex": "m", "born": 1668, "died": 1731, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"},
{"name": "Pieter Antone Haverbeke", "sex": "m", "born": 1753, "died": 1798, "father": "Jan Francies Haverbeke", "mother": "Petronella de Decker"},
{"name": "Maria van Brussel", "sex": "f", "born": 1801, "died": 1834, "father": "Jan Frans van Brussel", "mother": "Joanna de Causmaecker"},
{"name": "Angela Haverbeke", "sex": "f", "born": 1728, "died": 1734, "father": "Pieter Bernard Haverbeke", "mother": "Livina de Vrieze"},
{"name": "Elisabeth Haverbeke", "sex": "f", "born": 1711, "died": 1754, "father": "Jan Haverbeke", "mother": "Maria de Rycke"},
{"name": "Lievijne Jans", "sex": "f", "born": 1542, "died": 1582, "father":null, "mother":null},
{"name": "Bernardus de Causmaecker", "sex": "m", "born": 1721, "died": 1789, "father": "Lieven de Causmaecker", "mother": "Livina Haverbeke"},
{"name": "Jacoba Lammens", "sex": "f", "born": 1699, "died": 1740, "father": "Lieven Lammens", "mother": "Livina de Vrieze"},
{"name": "Pieter de Decker", "sex": "m", "born": 1705, "died": 1780, "father": "Joos de Decker", "mother": "Petronella van de Steene"},
{"name": "Joanna de Pape", "sex": "f", "born": 1654, "died": 1723, "father": "Vincent de Pape", "mother": "Petronella Wauters"},
{"name": "Daniel Haverbeke", "sex": "m", "born": 1652, "died": 1723, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"},
{"name": "Lieven Haverbeke", "sex": "m", "born": 1631, "died": 1676, "father": "Pieter Haverbeke", "mother": "Anna van Hecke"},
{"name": "Martina de Pape", "sex": "f", "born": 1666, "died": 1727, "father": "Vincent de Pape", "mother": "Petronella Wauters"},
{"name": "Jan Francies Haverbeke", "sex": "m", "born": 1725, "died": 1779, "father": "Pieter Bernard Haverbeke", "mother": "Livina de Vrieze"},
{"name": "Maria Haverbeke", "sex": "m", "born": 1905, "died": 1997, "father": "Emile Haverbeke", "mother": "Emma de Milliano"},
{"name": "Petronella de Decker", "sex": "f", "born": 1731, "died": 1781, "father": "Pieter de Decker", "mother": "Livina Haverbeke"},
{"name": "Livina Sierens", "sex": "f", "born": 1761, "died": 1826, "father": "Jan Sierens", "mother": "Maria van Waes"},
{"name": "Laurentia Haverbeke", "sex": "f", "born": 1710, "died": 1786, "father": "Jan Haverbeke", "mother": "Maria de Rycke"},
{"name": "Carel Haverbeke", "sex": "m", "born": 1796, "died": 1837, "father": "Pieter Antone Haverbeke", "mother": "Livina Sierens"},
{"name": "Elisabeth Hercke", "sex": "f", "born": 1632, "died": 1674, "father": "Willem Hercke", "mother": "Margriet de Brabander"},
{"name": "Jan Haverbeke", "sex": "m", "born": 1671, "died": 1731, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"},
{"name": "Anna van Hecke", "sex": "f", "born": 1607, "died": 1670, "father": "Paschasius van Hecke", "mother": "Martijntken Beelaert"},
{"name": "Maria Sturm", "sex": "f", "born": 1835, "died": 1917, "father": "Charles Sturm", "mother": "Seraphina Spelier"},
{"name": "Jacobus Bernardus van Brussel", "sex": "m", "born": 1736, "died": 1809, "father": "Jan van Brussel", "mother": "Elisabeth Haverbeke"}
]

25
labs/L09/loop-arith.js Normal file
View File

@ -0,0 +1,25 @@
function plus(a,b) {
for (let i=0; i < a; i++){
b++;
}
return b;
}
function mult(a,b) {
sum=0;
for(let i=0; i < a; i++) {
sum = plus(sum, b)
}
return sum;
}
function minus(a,b) {
for (let i=0; i < b; i++){
a--;
}
return a;
}
exports.plus = plus;
exports.mult = mult;
exports.minus = minus;

3
labs/L09/minus.js Normal file
View File

@ -0,0 +1,3 @@
let arith=require("./loop-arith.js");
console.log(arith.minus(10,0));

View File

@ -0,0 +1,8 @@
let fs = require('fs');
function read_json_file(filename) {
let contents = fs.readFileSync(filename);
return JSON.parse(contents);
}
exports.read_json_file=read_json_file;

View File

@ -0,0 +1,17 @@
let by_name=require("../ancestry.js").by_name;
describe("by_name", function() {
it("not-present", function() {
expect(by_name("ronald mcdonald")).toBe(null);
expect(by_name("Captain Ahab")).toBe(null);
});
it("first", function () {
expect(by_name("Carolus Haverbeke")).toEqual({"name": "Carolus Haverbeke", "sex": "m", "born": 1832, "died": 1905, "father": "Carel Haverbeke", "mother": "Maria van Brussel"});
});
it("last", function () {
expect(by_name("Jacobus Bernardus van Brussel")).toEqual({"name": "Jacobus Bernardus van Brussel", "sex": "m", "born": 1736, "died": 1809, "father": "Jan van Brussel", "mother": "Elisabeth Haverbeke"});
});
it("middle", function () {
expect(by_name("Joanna de Pape")).toEqual( {"name": "Joanna de Pape", "sex": "f", "born": 1654, "died": 1723, "father": "Vincent de Pape", "mother": "Petronella Wauters"});
});
});

View File

@ -0,0 +1,11 @@
describe("identity",
function() {
it("1 === 1", function() { expect(1).toBe(1); });
it("null === null", function() { expect(null).toBe(null); })
});
describe("arithmetic",
function() {
it("1 + 1 === 2", function() { expect(1 + 1).toBe(2); });
it("6 * 7 === 42", function() { expect(6*7).toBe(42); });
});

View File

@ -0,0 +1,43 @@
let arith=require ("../loop-arith.js");
describe("plus",
function() {
it("1 + 1 = 2",
function() {
expect(arith.plus(1, 1)).toBe(2);
});
it("0 + x = x",
function() {
expect(arith.plus(0, 1)).toBe(1);
});
});
describe("mult",
function() {
it("0 * 2 = 0",
function() {
expect(arith.mult(0, 2)).toBe(0);
});
it("1 * 2 = 2",
function() {
expect(arith.mult(1,2)).toBe(2);
});
it("2 * 2 = 4",
function() {
expect(arith.mult(2, 2)).toBe(4);
});
});
describe("minus",
function() {
it("1 - 1 = 0",
function() {
expect(arith.minus(1, 1)).toBe(0);
});
it("0 - x = -x",
function() {
expect(arith.minus(0, 1)).toBe(-1);
});
});

View File

@ -0,0 +1,13 @@
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.?(m)js"
],
"helpers": [
"helpers/**/*.?(m)js"
],
"env": {
"stopSpecOnExpectationFailure": false,
"random": true
}
}

18
labs/L10/arrays.js Normal file
View File

@ -0,0 +1,18 @@
function range(start, end, step=1) {
let result = [];
for (let i = start; i <= end; i += step) {
result.push(i);
}
return result;
}
function sum(array) {
let result = 0;
for (let i = 0; i < array.length; i++) {
result += array[i];
}
return result;
}
exports.range = range;
exports.sum = sum;

17
labs/L10/loop-arith.js Normal file
View File

@ -0,0 +1,17 @@
function plus(a,b) {
for (let i=0; i < a; i++){
b++;
}
return b;
}
function mult(a,b) {
sum=0;
for(let i=0; i < a; i++) {
sum = plus(sum, b)
}
return sum;
}
exports.plus = plus;
exports.mult = mult;

16
labs/L10/rec-arith.js Normal file
View File

@ -0,0 +1,16 @@
function plus(a,b) {
if (a === 0) {
return b;
} else {
return plus(a-1, b+1);
}
}
function mult(a,b) {
if (a === 0) {
return 0;
} else {
return plus(mult(a-1, b), b);
}
}
exports.plus = plus;
exports.mult = mult;

View File

@ -0,0 +1,31 @@
let array=require("../arrays.js");
describe("range", function () {
it("empty", function () {
expect(array.range(2,1)).toEqual([]);
});
it("single", function () {
expect(array.range(2,2)).toEqual([2]);
});
it("multiple", function () {
expect(array.range(42,50)).toEqual([42,43,44,45,46,47,48,49,50]);
});
it("step", function() {
expect(array.range(2,10,2)).toEqual([2,4,6,8,10]);
})
});
describe("sum", function () {
it("empty", function () {
expect(array.sum([])).toBe(0);
});
it("single", function () {
expect(array.sum([2])).toBe(2);
});
it("multiple", function () {
expect(array.sum(array.range(1,10))).toBe(55);
})
it("stepped", function() {
expect(array.sum(array.range(2,10,2))).toBe(30)
});
});

View File

@ -0,0 +1,30 @@
let arith=require ("../rec-arith.js");
describe("plus",
function() {
it("1 + 1 = 2",
function() {
expect(arith.plus(1, 1)).toBe(2);
});
it("0 + x = x",
function() {
expect(arith.plus(0, 1)).toBe(1);
});
});
describe("mult",
function() {
it("0 * 2 = 0",
function() {
expect(arith.mult(0, 2)).toBe(0);
});
it("1 * 2 = 2",
function() {
expect(arith.mult(1,2)).toBe(2);
});
it("2 * 2 = 4",
function() {
expect(arith.mult(2, 2)).toBe(4);
});
});

View File

@ -0,0 +1,31 @@
let loop_arith=require ("../loop-arith.js");
let rec_arith=require ("../rec-arith.js");
describe("plus",
function() {
it("1 + 1 = 2",
function() {
expect(loop_arith.plus(1, 1)).toBe(rec_arith.plus(1, 1));
});
it("0 + x = x",
function() {
expect(loop_arith.plus(0, 1)).toBe(rec_arith.plus(0, 1));
});
});
describe("mult",
function() {
it("0 * 2 = 0",
function() {
expect(loop_arith.mult(0, 2)).toBe(rec_arith.mult(0, 2));
});
it("1 * 2 = 2",
function() {
expect(loop_arith.mult(1, 2)).toBe(rec_arith.mult(1, 2));
});
it("2 * 2 = 4",
function() {
expect(loop_arith.mult(2, 2)).toBe(rec_arith.mult(2, 2));
});
});

View File

@ -0,0 +1,13 @@
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.?(m)js"
],
"helpers": [
"helpers/**/*.?(m)js"
],
"env": {
"stopSpecOnExpectationFailure": false,
"random": true
}
}

72
labs/L10/time.js Normal file
View File

@ -0,0 +1,72 @@
function timePlus(time1, time2) {
let mins = (time1.mins + time2.mins) % 60;
let hours = time1.hours + time2.hours + Math.floor((time1.mins+time2.mins)/60);
return {'hours': hours, 'mins': mins};
}
console.log(timePlus({hours: 10, mins:30}, {hours: 17, mins:47}));
function maketime(hours, mins){
let plus=function (other) {
let raw=timePlus(this,other);
return maketime(raw.hours, raw.mins);
};
return { 'hours': hours, 'mins': mins, 'plus': plus};
}
let A=maketime(10, 30);
let B=maketime(17, 47);
let C=A.plus(B);
console.log(C);
let protoTime = {
plus: function(other) {
let raw=timePlus(this,other);
return timeNew(raw.hours, raw.mins);
}
};
function timeNew(hours, mins) {
let obj=Object.create(protoTime);
obj.hours = hours;
obj.mins = mins;
return obj;
}
D=timeNew(21,42);
E=timeNew(17,37);
F=D.plus(E);
console.log(F);
function Time(hours, mins){
this.hours=hours;
this.mins=mins;
}
Time.prototype.plus=function (other) {
let raw=timePlus(this,other);
return new Time(raw.hours, raw.mins);
}
G = new Time(20,59);
H = new Time(11,11);
I=G.plus(H);
console.log(I);
class Time2 {
constructor(hours, mins){
this.hours=hours;
this.mins=mins;
};
plus(other) {
let raw=timePlus(this,other);
return new Time2(raw.hours, raw.mins);
}
}
J= new Time2(5,30);
K= new Time2(11,55);
L=J.plus(K);
console.log(L)

0
labs/L11/deep-equal.js Normal file
View File

View File

@ -0,0 +1,16 @@
describe("equal", function () {
let obj = {here: {is: "an"}, object: 2};
it("self", function () {
expect(deepEqual(obj,obj)).toBe(true);
});
it("null", function () {
expect(deepEqual(null,null)).toBe(true);
});
it("different", function () {
expect(deepEqual(obj, {here: 1, object: 2})).toBe(false);
});
it("equivalent", function () {
expect(deepEqual(obj, {here: {is: "an"}, object: 2})).toBe(true);
});
});

View File

@ -0,0 +1,13 @@
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.?(m)js"
],
"helpers": [
"helpers/**/*.?(m)js"
],
"env": {
"stopSpecOnExpectationFailure": false,
"random": true
}
}

View File

59
labs/L11/village.js Normal file
View File

@ -0,0 +1,59 @@
const roads = [
"Alice's House-Bob's House", "Alice's House-Cabin",
"Alice's House-Post Office", "Bob's House-Town Hall",
"Daria's House-Ernie's House", "Daria's House-Town Hall",
"Ernie's House-Grete's House", "Grete's House-Farm",
"Grete's House-Shop", "Marketplace-Farm",
"Marketplace-Post Office", "Marketplace-Shop",
"Marketplace-Town Hall", "Shop-Town Hall"
];
function buildGraph(edges) {
let graph = Object.create(null);
function addEdge(from, to) {
if (graph[from] == null) {
graph[from] = [to];
} else {
graph[from].push(to);
}
}
for (let [from, to] of edges.map(r => r.split("-"))) {
addEdge(from, to);
addEdge(to, from);
}
return graph;
}
const roadGraph = buildGraph(roads);
class VillageState {
constructor(place, parcels) {
this.place = place;
this.parcels = parcels;
}
move(destination) {
if (!roadGraph[this.place].includes(destination)) {
return this;
} else {
let parcels = this.parcels.map(p => {
if (p.place != this.place) return p;
return { place: destination, address: p.address };
}).filter(p => p.place != p.address);
return new VillageState(destination, parcels);
}
}
}
let first = new VillageState(
"Post Office",
[{ place: "Post Office", address: "Alice's House" }]
);
let next = first.move("Alice's House");
console.log(next.place);
// → Alice's House
console.log(next.parcels);
// → []
console.log(first.place);
// → Post Office

View File

@ -0,0 +1,13 @@
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.?(m)js"
],
"helpers": [
"helpers/**/*.?(m)js"
],
"env": {
"stopSpecOnExpectationFailure": false,
"random": true
}
}

View File

@ -0,0 +1,28 @@
let village = require("../village.js");
let VillageState = village.VillageState;
describe("roadGraph",
function () {
let roadGraph = village.roadGraph;
it("Alice's house",
() => expect(roadGraph["Alice's House"]).toEqual(["Bob's House", "Cabin", "Post Office"]));
it("Bob's house",
() => expect(roadGraph["Bob's House"]).toEqual(
jasmine.objectContaining(["Alice's House"])));
});
describe("VillageState",
function () {
let first = new VillageState(
"Post Office",
[{ place: "Post Office", address: "Alice's House" }]
);
let next = first.move("Alice's House");
it("next place",
() => expect(next.place).toBe("Alice's House"));
it("next parcels",
() => expect(next.parcels).toEqual([]));
it("first place",
() => expect(first.place).toEqual("Post Office"));
}
);

48
labs/L12/village.js Normal file
View File

@ -0,0 +1,48 @@
const roads = [
"Alice's House-Bob's House", "Alice's House-Cabin",
"Alice's House-Post Office", "Bob's House-Town Hall",
"Daria's House-Ernie's House", "Daria's House-Town Hall",
"Ernie's House-Grete's House", "Grete's House-Farm",
"Grete's House-Shop", "Marketplace-Farm",
"Marketplace-Post Office", "Marketplace-Shop",
"Marketplace-Town Hall", "Shop-Town Hall"
];
function buildGraph(edges) {
let graph = Object.create(null);
function addEdge(from, to) {
if (graph[from] == null) {
graph[from] = [to];
} else {
graph[from].push(to);
}
}
for (let [from, to] of edges.map(r => r.split("-"))) {
addEdge(from, to);
addEdge(to, from);
}
return graph;
}
const roadGraph = buildGraph(roads);
class VillageState {
constructor(place, parcels) {
this.place = place;
this.parcels = parcels;
}
move(destination) {
if (!roadGraph[this.place].includes(destination)) {
return this;
} else {
let parcels = this.parcels.map(p => {
if (p.place != this.place) return p;
return { place: destination, address: p.address };
}).filter(p => p.place != p.address);
return new VillageState(destination, parcels);
}
}
}
exports.roadGraph = roadGraph;
exports.VillageState = VillageState;

39
labs/L13/animation.js Normal file
View File

@ -0,0 +1,39 @@
// let str="";
// for (let i=0; i<60; i++) {
// console.log('\033c');
// str+= "*";
// console.log(str);
// }
//console.log("all done!");
// function loop(i,str) {
// if (i>0) {
// console.log("\033c");
// console.log(str);
// setTimeout(function() { loop(i-1, str+"*"); }, 1000);
// }
// }
// loop(20,"*");
// console.log("all done!");
function animate(iterations) {
let i=0;
let str="*";
let timer = null;
function frame() {
console.log('\033c');
console.log(str);
if (i>=iterations) {
clearInterval(timer);
console.log("all done!");
}
}
timer=setInterval(frame,300);
}
animate(20);

8
labs/L14/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

10
labs/L14/.idea/L14.iml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.10 (python-venv)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

12
labs/L14/.idea/dataSources.xml generated Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name=".coverage" uuid="af2f1fdd-3241-4470-93b5-53f58bcdd0d2">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:C:\Users\Isaac\OneDrive - University of New Brunswick\Year 3 UNB\CS2613\Git\cs2613-ishoebot\labs\L14\.coverage</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
labs/L14/.idea/misc.xml generated Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (python-venv)" project-jdk-type="Python SDK" />
</project>

8
labs/L14/.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/L14.iml" filepath="$PROJECT_DIR$/.idea/L14.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="client" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<module name="L14" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/client.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="fizzbuzz" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<module name="L14" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/fizzbuzz.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="humansize" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<module name="L14" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/humansize.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="pytest" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="pytest --cov --cov-report=term-missing" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
</component>

6
labs/L14/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

16
labs/L14/client.py Normal file
View File

@ -0,0 +1,16 @@
import humansize
def approximate_size(size):
"""Returns the size of a file in a human-readable format where kilobytes are 1000 bytes
:param size: the size of a file
:return: string
"""
return humansize.approximate_size(size, False)
if __name__ == '__main__': # pragma: no cover
print(approximate_size(1_000))
print(approximate_size(100_000_000))
print(approximate_size(1_000_000_000))

8
labs/L14/divisive.py Normal file
View File

@ -0,0 +1,8 @@
import math
def fraction(a, b):
try:
return a / b
except ZeroDivisionError:
return math.nan

10
labs/L14/fizzbuzz.py Normal file
View File

@ -0,0 +1,10 @@
if __name__ == '__main__':
for i in range(1, 101):
if i % 3 == 0 and i % 5 == 0:
print("FizzBuzz")
elif i % 3 == 0:
print("Fizz")
elif i % 5 == 0:
print("Buzz")
else:
print(i)

67
labs/L14/humansize.py Normal file
View File

@ -0,0 +1,67 @@
"""Convert file sizes to human-readable form.
Available functions:
approximate_size(size, a_kilobyte_is_1024_bytes)
takes a file size and returns a human-readable string
Examples:
>>> approximate_size(1024)
'1.0 KiB'
>>> approximate_size(1000, False)
'1.0 KB'
"""
SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
def approximate_size(size, a_kilobyte_is_1024_bytes=True):
"""Convert a file size to human-readable form.
Keyword arguments:
size -- file size in bytes
a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
if False, use multiples of 1000
Returns: string
"""
if size < 0:
raise ValueError('number must be non-negative')
multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
for suffix in SUFFIXES[multiple]:
size /= multiple
if size < multiple:
return '{0:.1f} {1}'.format(size, suffix)
raise ValueError('number too large')
if __name__ == '__main__': # pragma: no cover
print(approximate_size(1000000000000, False))
print(approximate_size(1000000000000))
# Copyright (c) 2009, Mark Pilgrim, All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

18
labs/L14/test_client.py Normal file
View File

@ -0,0 +1,18 @@
import pytest
from client import approximate_size
def test_1kb():
assert approximate_size(1_000) == "1.0 KB"
def test_100mb():
assert approximate_size(100_000_000) == "100.0 MB"
def test_1gb():
assert approximate_size(1_000_000_000) == "1.0 GB"
def test_docstring():
assert approximate_size.__doc__ is not None

10
labs/L14/test_divisive.py Normal file
View File

@ -0,0 +1,10 @@
from divisive import fraction
import math
def test_fraction_int():
assert fraction(4, 2) == 2
def test_fraction_NaN():
assert math.isnan(fraction(4, 0))

View File

@ -0,0 +1,20 @@
import pytest
from humansize import approximate_size
def test_1000():
assert approximate_size(1000000000000, False) == "1.0 TB"
def test_1024():
assert approximate_size(1000000000000) == "931.3 GiB"
def test_negative():
with pytest.raises(ValueError):
approximate_size(-1)
def test_huge_number():
with pytest.raises(ValueError):
approximate_size(1_000_000_000_000_000_000_000_000_000_000)

8
labs/L15/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
labs/L15/.idea/L15.iml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.10 (python-venv)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
labs/L15/.idea/misc.xml generated Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (python-venv)" project-jdk-type="Python SDK" />
</project>

8
labs/L15/.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/L15.iml" filepath="$PROJECT_DIR$/.idea/L15.iml" />
</modules>
</component>
</project>

Some files were not shown because too many files have changed in this diff Show More