Unnamed Fossil Project

Check-in [2fa5357a8e]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Start working up items, DB, and authentication
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:2fa5357a8ed8cf2c6d200643710cf125e894ecdcdaa0730acc4858171c9aab27
User & Date: vandys 2019-06-18 22:59:45
Context
2019-06-18
23:00
Clean "fs extras" check-in: 462da1bee0 user: vandys tags: trunk
22:59
Start working up items, DB, and authentication check-in: 2fa5357a8e user: vandys tags: trunk
2019-06-16
17:04
Initial shape of source repo check-in: 8a259a1618 user: vandys tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to css/shopr.css.

6
7
8
9
10
11
12









}
#items > span {
 flex-grow: 0;
}
.isel {
 flex-grow: 1;
}
















>
>
>
>
>
>
>
>
>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
}
#items > span {
 flex-grow: 0;
}
.isel {
 flex-grow: 1;
}
.clR {
 background: pink;
}
.clG {
 background: LightGreen;
}
.clY {
 background: yellow;
}

Changes to get.py.

1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37




38
39
40
41
42
43
44
#
# get.py
#	Mixin to implement HTML GET operations
#
# Structure of paths in the server:
#  /
#	List of lists
#  /<listname>/<col#>
#	UI for a particular list at the given column

import sys, urllib
import ilist, delta

# The GET part of our handling
class GET_mixin(object):

    def __init__(self):
	# /<listname>/<col #/name>
	#	Web page with view of this list's column
	self.dispatchers.append( ("GET", self.send_list) )

	# /rest/l.json
	#	JSON List of lists
	self.dispatchers.append( ("GET", self.send_json_lists) )

	# /rest/l/<listname>/<col# | name>.json
	#	JSON Dope for a given list
	self.dispatchers.append( ("GET", self.send_json_list) )

	# Long polling
	self.dispatchers.append( ("GET", self.send_json_changes) )

    # Root web page, redir to our static HTML to get the
    #  list of lists
    def send_top(self):
	return self.send_redir("/html/v_ilists.html")

    # Display the named list




    def send_list(self):
	pp = self.paths
	if (len(pp) != 3) or (pp[0] != 'l'):
	    return False,None

	# Not a known list
	nm = pp[1]




|
<
<
<
<
>
|
|





|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>







1
2
3
4
5




6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#
# get.py
#	Mixin to implement HTML GET operations
#
#	One page webapp




#
import sys
import items

# The GET part of our handling
class GET_mixin(object):

    def __init__(self):

	# /l/<listname>.json
	self.dispatchers.append( ("GET", self.send_list) )

	# /lists.json
	self.dispatchers.append( ("GET", self.send_lists) )

    # Root web page, single page webapp
    def send_top(self):
	return self.send_files("/html/shopr.html")

    # /lists.json
    #
    # Return list of lists
    def send_lists(self):
	if not self.path_match("lists.json"):
	    return False,None

	if not self.authenticated():
	    return True,self.send_error(403)

    # /<listname>.json[?gen=<number>]
    #
    # Get <listname>'s contents, waiting for new contents if
    #  nothing newer than generation <number> exists yet.
    def send_list(self):
	pp = self.paths
	if (len(pp) != 3) or (pp[0] != 'l'):
	    return False,None

	# Not a known list
	nm = pp[1]

Added items.py.































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#
# items.py
#	Represent the items on a shopping list
#
import sqlite3

class List(object):
    def __init__(self, approot, name):
	self.approot = approot
	self.name = name
	self.items = []
	self.gen = 0

	# Load any existing state
	gen = -1
	db = self.get_db()
	c = db.cursor()
	c.execute("select name,idx,gen from items where list=?", name)
	for tup in c:
	    iname,idx,igen = tup

	    # Deleted items shouldn't be in the DB
	    assert idx >= 0

	    # New item, with the saved generation value
	    i = Item(self, idx, iname, igen)
	    gen = max(gen, igen)

	# The next generation is one beyond any previously allocated.
	# As a special case, we advance -1 to 0 to get an initial
	#  value for an empty DB.
	self.gen = gen+1

	c.close()
	db.close()

    # Return a DB connection
    def get_db(self):
	return sqlite3.connect(self.approot.dbname)

    # Allocate the next generational value for list state
    def next_gen(self):
	res = self.gen
	self.gen += 1
	return res

class Item(object):
    def __init__(self, l, idx, name, gen):

	# Our parent list
	self.parent = l

	# Always start at first index type (usually red)
	# When -1, this is a deleted item
	self.idx = 0

	# Item's string name
	self.name = name

	# Generation of change
	self.gen = gen

    # Save ourselves to the DB
    def sync(self):
	db = self.parent.get_db()
	c = db.cursor()
	c.execute("insert or replace into items values(?,?,?,?) ",
	    (self.parent.name, self.name, self.idx, self.gen))
	db.commit()
	c.close()
	db.close()

    def assign(self, idx):
	# No change
	if self.idx == idx:
	    return

	# New value & generation
	self.idx = idx
	self.gen = self.parent.next_gen()

	# Put into DB
	self.sync()

# Shared state for any given list name
Lists = {}
def get_list(approot, name):
    global Lists

    l = Lists.get(name)
    if l is None:
	l = List(approot, name)
    else:
	assert l.approot is approot
    return l

Changes to main.py.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#
# main.py
#	Main driver for list/swiping service
#
import sys
import chore
from chore.authen import Authen_mixin, Authen_Server_mixin
from get import GET_mixin
from post import POST_mixin
from put import PUT_mixin
import ilist

# Tie our various handlers together
class App_Handler(chore.handlers.Chore_Handler,
	GET_mixin, POST_mixin, PUT_mixin, Authen_mixin):
    def __init__(self, conn, tup, approot):
	chore.handlers.Chore_Handler.__init__(self, conn, tup, approot,
	    (GET_mixin.__init__,
	     POST_mixin.__init__,
	     PUT_mixin.__init__,
	     Authen_mixin.__init__,) )

# Load our configuration file
#
# This includes configuring our config file elements,
#  then processing the supplied file.
def load_cfg(fn):

    # A configurator
    c = chore.config.Config()

    # Let the web network side add its config entries
    chore.www.add_config(c)

    # Parse the input
    return c.load_cfg(fn)

# Root of our app server
class App(chore.server.Server, Authen_Server_mixin):

    def __init__(self, config):



	# Let Chore handle most things
	chore.server.Server.__init__(self, config, App_Handler);

	# Authentication server
	Authen_Server_mixin.__init__(self)
	self.authentication.append( Authen_mixin.auth_server )
	self.init_acct_server()

if __name__ == "__main__":
    if len(sys.argv) != 2:
	sys.stderr.write("Usage is: %s <config-file>\n" %
	    (sys.argv[0],))
	sys.exit(1)

    # Create the server with config
    app = App(load_cfg(sys.argv[1]))

    # Pull current list directory
    ilist.load_ilists()

    # It's an HTTP service
    app.start_http()

    # HTTP threads remain
    sys.exit(0)






<



|



|




|
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|

|
>
>




<
<
<
<
<


|




|
<
<
<






1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19

20















21
22
23
24
25
26
27
28
29
30





31
32
33
34
35
36
37
38



39
40
41
42
43
44
#
# main.py
#	Main driver for list/swiping service
#
import sys
import chore

from get import GET_mixin
from post import POST_mixin
from put import PUT_mixin
import items

# Tie our various handlers together
class App_Handler(chore.handlers.Chore_Handler,
	GET_mixin, POST_mixin, PUT_mixin):
    def __init__(self, conn, tup, approot):
	chore.handlers.Chore_Handler.__init__(self, conn, tup, approot,
	    (GET_mixin.__init__,
	     POST_mixin.__init__,
	     PUT_mixin.__init__,) )

















# Root of our app server
class App(chore.server.Server):

    def __init__(self, dbn):
	# sqlite3 database file
	self.dbname = dbn

	# Let Chore handle most things
	chore.server.Server.__init__(self, config, App_Handler);






if __name__ == "__main__":
    if len(sys.argv) != 2:
	sys.stderr.write("Usage is: %s <file.db>\n" %
	    (sys.argv[0],))
	sys.exit(1)

    # Create the server with config
    app = App(sys.argv[1])




    # It's an HTTP service
    app.start_http()

    # HTTP threads remain
    sys.exit(0)

Added mkdb.sql.





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
create table users(
    uid integer primary key autoincrement,
    name text,
    pw text,
    unique(name)
);

create table items(
    list text,
    name text,
    idx integer,
    gen integer,
    unique(list,name)
);