Compare commits

...

37 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
112 changed files with 5953 additions and 18 deletions

3
.gitattributes vendored
View File

@ -2,4 +2,5 @@
* text eol=lf * text eol=lf
*.png binary *.png binary
*.jpg binary *.jpg binary
*.exe binary

10
.gitignore vendored
View File

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

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,16 +0,0 @@
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 -->
## Methods
## Prototypes
## Classes
## Arrays
## Recursion

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.

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>

View File

@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="globex" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<module name="L15" />
<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="C:\Users\Isaac\Documents\CS2613-Repo\cs2613-ishoebot\labs\L15\globex.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/L15/.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>

22
labs/L15/globex.py Normal file
View File

@ -0,0 +1,22 @@
import glob
import os
# new_dir = os.path.expanduser("~/fcshome/cs2613/labs/test") # For lab machines
new_dir = os.path.abspath("C:\\Users\\Isaac\\Documents\\CS2613-Repo\\cs2613-ishoebot\\labs\\L14") # For local machine
python_files_for = []
for file in glob.glob("*.py"):
python_files_for.append(os.path.join(new_dir, file))
python_files_comp = [os.path.join(new_dir, file) for file in glob.glob("*.py")]
python_files_map = map(lambda file: os.path.join(new_dir, file), glob.glob("*.py"))
if __name__ == '__main__': # pragma: no cover
print(python_files_for)
print()
print(python_files_comp)
print()
print(list(python_files_map))

7
labs/L15/list2dict.py Normal file
View File

@ -0,0 +1,7 @@
def list2dict(lst):
lst_dict = dict()
counter = 1
for i in range(lst):
lst_dict[counter] = i
counter += 1
return lst_dict

9
labs/L15/test_globex.py Normal file
View File

@ -0,0 +1,9 @@
import globex
def test_for():
assert sorted(globex.python_files_for) == sorted(globex.python_files_comp)
def test_map():
assert sorted(globex.python_files_comp) == sorted(globex.python_files_map)

View File

@ -0,0 +1,10 @@
from list2dict import list2dict
def test_empty():
assert list2dict([]) == {}
def test_abc():
dictionary = list2dict(["a", "b", "c"])
assert dictionary == {1: 'a', 2: 'b', 3: 'c'}

8
labs/L16/.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/L16/.idea/L16.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,12 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E501" />
</list>
</option>
</inspection_tool>
</profile>
</component>

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/L16/.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="CS2613-venv" project-jdk-type="Python SDK" />
</project>

8
labs/L16/.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/L16.iml" filepath="$PROJECT_DIR$/.idea/L16.iml" />
</modules>
</component>
</project>

6
labs/L16/.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>

14
labs/L16/parse_csv.py Normal file
View File

@ -0,0 +1,14 @@
import re
def split_csv(string):
return [row.split(",") for row in string.splitlines()]
def strip_quotes(string):
strip_regex = re.compile(r'("?)*$("?)')
search = strip_regex.search(string)
if search:
return search.group(1)
else:
return None

View File

@ -0,0 +1,45 @@
from parse_csv import split_csv
from parse_csv import strip_quotes
test_string_1 = """OPEID,INSTNM,TUITIONFEE_OUT
02503400,Amridge University,6900
00100700,Central Alabama Community College,7770
01218200,Chattahoochee Valley Community College,7830
00101500,Enterprise State Community College,7770
00106000,James H Faulkner State Community College,7770
00101700,Gadsden State Community College,5976
00101800,George C Wallace State Community College-Dothan,7710
"""
table1 = [['OPEID', 'INSTNM', 'TUITIONFEE_OUT'],
['02503400', 'Amridge University', '6900'],
['00100700', 'Central Alabama Community College', '7770'],
['01218200', 'Chattahoochee Valley Community College', '7830'],
['00101500', 'Enterprise State Community College', '7770'],
['00106000', 'James H Faulkner State Community College', '7770'],
['00101700', 'Gadsden State Community College', '5976'],
['00101800', 'George C Wallace State Community College-Dothan', '7710']]
def test_split_1():
assert split_csv(test_string_1) == table1
test_string_2 = '''OPEID,INSTNM,TUITIONFEE_OUT
02503400,"Amridge University",6900
00100700,"Central Alabama Community College",7770
01218200,"Chattahoochee Valley Community College",7830
00101500,"Enterprise State Community College",7770
00106000,"James H Faulkner State Community College",7770
00101700,"Gadsden State Community College",5976
00101800,"George C Wallace State Community College-Dothan",7710
'''
def test_split_2():
assert split_csv(test_string_2) == table1
def test_strip_quotes():
assert strip_quotes('"hello"') == 'hello'
assert strip_quotes('hello') == 'hello'

8
labs/L17/.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/L17/.idea/L17.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="CS2613-venv" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,12 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E501" />
</list>
</option>
</inspection_tool>
</profile>
</component>

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/L17/.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="CS2613-venv" project-jdk-type="Python SDK" />
</project>

8
labs/L17/.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/L17.iml" filepath="$PROJECT_DIR$/.idea/L17.iml" />
</modules>
</component>
</project>

6
labs/L17/.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/L17/main.py Normal file
View File

@ -0,0 +1,16 @@
# This is a sample Python script.
# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
def print_hi(name):
# Use a breakpoint in the code line below to debug your script.
print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint.
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
print_hi('PyCharm')
# See PyCharm help at https://www.jetbrains.com/help/pycharm/

View File

@ -0,0 +1,22 @@
#lang racket
(define (balance lst)
(define (helper lst counter)
(cond
[(empty? lst) counter] ;;base case
[(list? (first lst)) (helper (rest lst) counter)] ;;unwrap list
[(eq? (first lst) 'debit) (helper (rest lst) (- counter last))] ;;if debit subtract the amount
[(eq? (first lst) 'credit) (helper (rest lst) (+ counter last))] ;;if credit add the amount
[else (helper (rest lst) counter)]
)
)
(helper lst 0)
)
;; Racket
(module+ test
(require rackunit)
(check-equal? (balance (list (list 'credit 5))) 5)
(check-equal? (balance (list '(debit 5))) -5)
(check-equal? (balance '((debit 11) (credit 3))) -8)
(check-equal? (balance '((debit 3) (credit 5))) 2)
(check-equal? (balance '((debit 5) (credit 23) (debit 23) (credit 5))) 0))

View File

View File

@ -0,0 +1,19 @@
def invert(lst):
lst = list(lst) # Makes sure arugment is a list
#list_range = range(0, len(lst)) # Code I might have used if I used a dictionary comprehension
ret_dict = dict() # Create empty dict
counter = 0 # Create counter
for i in lst:
ret_dict[i] = counter # Assign each element of list to
# its postion in list
counter = counter + 1 # Increment counter
if (len(lst) > len(ret_dict)): # Check if the length of new dict is less than
return None # input list, if so, there is duplicates so return none
return ret_dict # Return created dictionary

View File

@ -0,0 +1,13 @@
from invert import invert
def test_empty():
assert invert([]) == {}
def test_simple():
invert(["three","two","one"]) == {"three": 0, "two":1, "one":2}
def test_duplicate():
assert invert(["bob","bob"]) == None
def test_numeric():
assert invert(range(0,6)) == { 0:0, 1:1, 2:2, 3:3, 4:4, 5:5 }
def test_invert():
L=[-8,"pineapple",3]
D=invert(L)
assert [ L[D[j]] for j in L ] == L

View File

@ -0,0 +1,50 @@
class Expression {
constructor(op, left, right) {
this.op = op
this.left = left
this.right = right
}
eval() {
return evalExpression(this)
}
}
function evalExpression(expr) {
let tempLeft
let tempRight
if (typeof(expr.left) === "object") { // Check if left type is another expression
tempLeft = evalExpression(expr.left)
}
else {
tempLeft = expr.left
}
if (typeof(expr.right) === "object") { // Check if right type is another expression
tempRight = evalExpression(expr.right)
}
else {
tempRight = expr.right
}
if (typeof(tempLeft) === "number" & typeof(tempRight) === "number") { // Make sure both inputs are number
if (expr.op === "+") {
return tempLeft + tempRight
}
else if(expr.op === "-") {
return tempLeft - tempRight
}
else if(expr.op === "*") {
return tempLeft * tempRight
}
else if(expr.op === "/") {
return tempLeft / tempRight
}
else { // Case for when there is no valid provided operator
return "Invalid operator syntax"
}
}
else { //Case for when the left or right are not numbers
return "Invalid number syntax"
}
}
exports.Expression = Expression;

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