Open kamn opened 10 years ago
I think you should meet with the front-end team and try to figure out exactly what the division of labor is. I figured that they would start by writing the HTML/js files that will eventually be served by your back-end server once it's working.
2014/1/24 Samuel Miller notifications@github.com
I am just going to kick off the discussion for the Backend team(David, Mike and me) so everyone else can ignore this thread. I just wanted to mention what I am doing, an ideas and some questions.
First, I am going over a Flask tutorialhttp://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-worldwhich mentions some modules that might be useful. Once I am done I will give a quick overview.
Second, I think that we should try to get some simple open-source 311 api server running for the front-end team. To give them something to work with early. Like I have mentioned I think thishttps://github.com/codeforamerica/open-311-api-server might work but I am not sure. I will try to get that up and working after the Flask tutorial(or after reading the Open311 specs)
Third, the backend is going to be at least partially an API but is it only going to be an API? For example, are we going to allow a simple interface for viewing databases or managing issues(like closing them or reassigning them)? Or is that the front end's job? If we are going to be adding some interactive features, which? I feel like parts of this were discussed but I would like to see something in writing.
Fourth, I have not gotten to the Open311 spec yet but probably will by the end of the weekend but has anything stood out(important to note) to either of you guys?
— Reply to this email directly or view it on GitHubhttps://github.com/byu-osl/city-issue-tracker/issues/1 .
Sam, for now I think the important things for the back-end team to work on is to get up to speed on Flask, the Open311 spec and any other possible code out there. I'm going to spend some time tonight reacquainting myself with Flask and then digging through the Open311 spec. Tomorrow I'm going to go through codeforamerica's stuff and see if they have anything we can build from. I think that we shouldn't try to make decisions until we are conversant in our area. That way we have opinions and discussion to contribute to the entire team.
Kyle, thanks for your input. Our sub-team has some stuff to research and learn before we will be able to contribute to the entire team. We have work to keep us busy and we'll get back to the team when we have more to contribute.
I was going to post my DB schema for the DB but I let my home computer is not responding to my SSH so I will post that when I get home. It was basically similar to things we discussed.
This post will mainly related to the topic of MongoDB and the ORMs we could use for it.
There is a page on the MongoDB that talks about several ORM Layers. I will note the ones here that seem to have the most following
I think that MongoEngine is the best choice since it seems to be maintained fairly well(last commit was 3 days ago while other where months ago). There is one draw back that is pointed out in a stackoverflow comment. You can't randomly add attributes with MongoEngine. If we want that it might be best to just use straight PyMongo.
I'm totally fine if we just go with straight PyMongo. I've done a little of that and it was pretty simple. I still am not convinced between SQL and Mongo myself, though I am leaning more towards Mongo than SQL. @mkeagar any thoughts?
If you choose Mongo, then I would strongly suggest PyMongo. I've used the other wrappers and they are not as mature and stable. I can show you some simple model code in Flask using PyMongo.
I'm not convinced this app needs Mongo. It seems like a good fit for MySQL.
-- Daniel Zappala
On Fri, Jan 31, 2014 at 8:33 PM, David Barley notifications@github.comwrote:
I'm totally fine if we just go with straight PyMongo. I've done a little of that and it was pretty simple. I still am not convinced between SQL and Mongo myself, though I am leaning more towards Mongo than SQL. @mkeagarhttps://github.com/mkeagarany thoughts?
Reply to this email directly or view it on GitHubhttps://github.com/byu-osl/city-issue-tracker/issues/1#issuecomment-33862443 .
When you have a finalized data model it would be good to update the section of the requirements doc that discusses it. Also, Cameron and I already have some db requirements based on what the city said.
I know I'm just the peanut gallery on this one, but I'm pretty sure that a document store like Mongo is going to make life harder overall. The app needs to be able to retrieve issues by different keys like date, status and user to name a few. Also, if multiple users are following multiple issues you're going to need to do either a lot of denormalization or manual joins, either of which have their drawbacks. I think SQL will work better here.
@zappala We were discussing MySQL vs Mongo because there is the possibility that a city could add unique attributes to a service as specified in the Service Definition spec. This can be done with a many-to-one relationship in MySQL but we thought the dynamic nature of Mongo might help in this aspect and wanted to explore it a little. We also were going to ask you about your database preference and after your comments I think we will probably end up using MySQL with sqlaclhemy. What do you think @ghuitster @mkeagar ?
I wanted to echo what @kamn said about us considering Mongo based on the flexibility the Open311 spec gives cities to create their own service types with associated custom metadata. Our initial gut reaction was MySQL so we wanted to make sure we had thought about the alternatives and the associated pros and cons.
A few interesting things I've seen in some research this morning: This repo has some beginnings of what we want. It's a Flask app that has has functions for routing and some basic templates. We could start with this just to save some keystrokes. This wiki page has a list of good resources for developers to look at. Found additional Android and iOS open source apps. This repo has a full implementation of an Open311 server in PHP. In regards to the discussion we had above, they use MySQL. Another cool repo has an object that gives easy interface with an Open311 server.
@ghuitster Good finds. I looked at the DB for the uReport(the PHP one) and here is what their database sort of looks like. It should be a good comparison against our designs. In fact we could just use a design similar to theirs but I feel like there are some thing that we don't need(people's address and phone numbers). None the less I said I would try to create some outline of a schema so...
@ghuitster @mkeagar Here is a general idea of how we could have a database setup. I am not a MySQL Master(very little experience) and it seems like Mike has a lot of experience already so feel free to correct and modify it. Here is the site I used to make it and here is the XML that you can load in to play around with. I have been working on this over a course of a week(on and off). I see a few errors already when posting this. This is meant to give an overview of the tables and key(but not the type of keys. That I just ignored).
The Users
table is wrong since we probably want a login_hash
field for admin and the Employee
table could probably be completely removed with the Roles
table also. As we discussed we can use the Token(I forgot to add this is the database) and OpenID methods for regular users with a secure login for admins. Also for the regular user do we just want to leave the subscriptions open like Chicago does? It is defiantly a possibility of abuse(ex. "Congrats David, you are now subscribed to every issue of every Open311 city") but should we just ignore it for now? It's not in the spec to see a users subscriptions and maybe just include a unsubscribe link at the bottom of each email(which is what Chicago does)
The Jurisdictions
table is included from before I realized that we probably won't focus on that aspect.
The right side I think has everything that would be needed for the Open311 script. I tried to keep the names as closely as I could related to what they are called in the specs. I have a many to one mapping for Service Definitions spec in ServiceAttributes
with the ServiceAttributeKeys
. The Tokens
table is meant to be used as a way to hide the ServiecRequest
until approved(because there is something like that in Open311) There is some additional info that needs to be in the Tokens
table or maybe we should just have a visible
key.
I also forgot a table for the Open311 API Security keys.
It's just an idea so feel free to question it, hack at it or expand some parts of it. The only part that I feel should be kept is having names similar to how the Open311 spec names things. Did you guys(Mike and David) come up with any ideas?
Also that schema does not have a table for media types
I came up with one that looks almost exactly like the png you posted. Yours is more complete than mine as far as links and such though. After class tomorrow can we start with the schema you've got and tweak it?
Hey guys, sorry I haven't jumped in to comment yet. I'm catching up now on everything you've posted.
As I mentioned in person, the extension that would have seemed to work best for migration and models to DB conversion is not what we originally thought(which was sqlalchemy-migrate). Instead, we should probably use Alembic which is maintained by the same guy that does SQLAlchemy. Here is a post about it if you would like to read more.
I have used it somewhat and I will probably play with it some more. I am still unsure of it but if worst comes to worst we can just manually create the databases.
I have put up some fake data. I still need to put up some basic user fake data though. After that the issue of how we divide models comes up. How should we do it? Who should take what? I figure there are the following basic models(I sort of nested some into groups)
User
Subscription
Service
ServiceAttribute
ServiceAttributeKey
ServiceRequest
Media
Then there are departments and stuff...which we don't have to add now. I guess we will discuss more on Thursday but if either one of you wants to lay claim to some of these go for it.
Oh, either one of you know if it's ok to mess with the README.md? I figure it should need some run instructions sometime soon
You're welcome to mess with the README.md anytime you want! It's easy to undo any change if we don't like it. I have install instructions in install/instructions.txt but no running instructions. I think we should talk more on Thursday about diving up the models and such, I dunno if a conversation thread will work very well for something like that.
Also I want to throw out I am working on an install script for the application. It will have the option to create a virtualenv or do a global install and create(maybe just copy) a simple run.py in the base dir. Just giving you guys a heads up.
The install script is up. It installs a virtualenv for python and then creates a run.py. It should be working. Just type python install_cit.py
and afterwards ./run.py
. For now it only works on Linux. It does stuff in a way that I am not sure I like so please review and modify it. The only other thing I was thinking of making it do is forcing itself to respawn if it is killed but that will be put on the back burner.
So I can't remember, was it @mkeagar on Users, @kamn on Service and me on ServiceRequest?
As far as I remember I had rolled for Users.
I had Service
Also have any of you started? If you had could you push the models/
folder please
I haven't started yet, but should be able to later this evening.
I was looking at the backend and how the code is structured. Currently it is setup in such a way that app/app.py
is the main file. This file holds everything almost. It creates the Flask instance and has all the routes(or views). I was originally thinking that we could have a folder app/models
which would contain files like app/models/user.py
or app/models/serviceRequest.py
.
We are using an extension called Flask-SQLAlchemy and this is what it says on the website about how to this extension with Flask...
For the common case of having one Flask application all you have to do is to create your Flask application, load the configuration of choice and then _create the SQLAlchemy object by passing it the application_.
We need to activate this extension by passing in the Flask app then we can start building our models and here is there the problem is. In order to build the models we need the Flask-SQLAlchemy db which needs the Flask App. This is created and stored in app/app.py
which then uses a model like app/models/service.py
but service.py
need the Flask-SQLAlchemy db which is stored in app/app.py
so you get a sort of circular dependency.
I have tried to work around this but it was sort of hackish and I couldn't seem to get it working completely normal. I could only import the models after all the routes had been set which is sort of useless. If either of you want I can get on chat and explain how I tried to change the current app/app.py
to work and failed.
I was wondering if it would be good to change the way we have the app structured. Instead of having a app/app.py
we could create a app/__init__.py
with the app
and db
instances and that would be the top of the hierarchy. Everything could use from app import app, db
and not break. Most of the functionality in app/app.py
could be moved into something like app/views.py
or app/routes.py
. The models would also have no problem being loading in places like app/views.py
since the models will inherent from app/__init__.py
I think I am heavily tainted from my experience with that Flask Mega-tutorial (because the proposed change is how he had his structure). I don't have experience with other Flask examples really. I have done this on my local repo but I wanted to get some thoughts before I committed something like this. Sound like a bad or good idea?
It sounds like a good idea to me. I'm basing the structure off my experience last semester, but change is totally fine! I'm on chat now if you want to talk as well. I think that using init is the proper Python way of doing things, I think.
I agree as well. We're all python newbs, pretty much, but I think we can recognize when one of us has a good idea. On Feb 22, 2014 11:07 AM, "David Barley" notifications@github.com wrote:
It sounds like a good idea to me. I'm basing the structure off my experience last semester, but change is totally fine! I'm on chat now if you want to talk as well. I think that using init is the proper Python way of doing things, I think.
Reply to this email directly or view it on GitHubhttps://github.com/byu-osl/city-issue-tracker/issues/1#issuecomment-35809537 .
I few things that maybe you guys would like to know... and one question
First, some of the Open311 specifications have some Enums like Service
with type
. The only valid options are realtime
, batch
, and blackbox
. SQLAlchemy has a way to validate this with validators. You can see an example in the Service.py model.
Second, I found out Flask has a method called jsonify
which is good. There is one issue however. It refuses to return arrays. The reason is here. I think it's a frustrating reason that I don't think we need to worry about too much. It is however the default behaviour can I can't see to see any way to change this. We can use json.dump
also which is in python. I haven’t found much from python to XML though.
Third, should we just create some very basic test for our models? I know that falls under what Cameron is doing but I am just trying to get a feel for how it's working.
Good discoveries for models and json, that's very helpful to know. I'm digging into my model section now. On that note, am I making models that correspond just to the API endpoints for service requests?
I'm surprised there isn't some nice module for doing json to xml, I'll take a look as well and see what I can find.
I think it's fine to create basic tests for your models, most programmers write a few unit tests for their own stuff.
I actually did find a python module that seems to work fairly well called xmltodict. It has an unparse
method that converts JSON to XML . I have added it to the requirements for now. The next issue I ran into next was getting the SqlAlchemy models into JSON. There are several discussions on Stackoverflow solutions but it seems like they don't work very well if you have some complex relationships. I have not been looked at it too closely and it will probably be my last step in finishing my models.
I have also spent a lot of time today reviewing models and specifically the creation relationships between (one-to-many and many-to-many). If you need some references check the app/models/service.py
and app/models/serviceattr.py
When you are testing your models you might want to create a temp sqllite database. The code for this should be in the app/__init__.py
commented out. Just uncomment it and added db.create_all()
to app/views.py
Sorry, I forgot to answer your question about the API endpoints. Yea that's what I pretty much did. I sort of followed the SQL database and tables diagram from earlier. I do have some questions about the end-point but we will discuss those in person sometime.
Holy cow I just had a fun time getting the app to run. Learned something. Table names are class names, BUT convert TableName to table_name. You can specify a table name in the class by saying [two_underscores]tablename[two_underscores] = "Name" and that sticks. Can't get the two underscores to display, it just bolds the word.
So do we want all the tables(as in inside the DB) to be camelCased also? also you can make the underscores show up if you use a tick . For example
tablename`. or just regular backslash works also. tablename
Also I was wondering about how to handle turning out models into Python dictionaries to convert to json. At first I though that we could maybe just do something like this but I am unsure how it would turn out with the relationships we have setup. Maybe it will send a ton of information that maybe we didn't want it to send. For example maybe the Service Attributes and that is an array of python objects which would then need to be detected and serialized which conatins the Service Attribute Values which would have to be detected and serialized.
I am not quite sure what I solution would be. Maybe a special function (like __repr__
?) that needs to be defined for each? I just wanted to get some discussion going on about this.
I'm fine if we do camel case in the DB, but I'm also fine doing underscores. Do you have a preference?
__repr__
is the typical way of doing something like that. We could have that method accept an argument for XML or JSON type to be returned and then do the construction there? I have no idea if there's a good library for this. For now we could just return a string that we construct ourselves and then look into getting a library later?
About time I jumped into the fray here...
Anyhow, I've been doing testing when I've started experiencing problems in the user. The funny thing is, it's the email that's the issue. I've tried
user = models.User( userId=1, firstName='Cameron', lastName='Jones', email='cejonesmsncom', phone='7192007970', passwordHash='60744474c3277428a8be861186e1e368', passwordSalt='s0mRIdlKvI', role='user', lastLogin=date.today(), joined=date.today())
and it throws
Traceback (most recent call last):
File "cameron_testing.py", line 68, in test_User
joined=date.today())
File "
However, if I comment out the email, then it doesn't have a problem. I don't know why email would be a problem without other string values like firstName or role being issues as well. Comments?
I'll take a look after class. On Mar 11, 2014 12:23 PM, "Cameron Jones" notifications@github.com wrote:
About time I jumped into the fray here...
Anyhow, I've been doing testing when I've started experiencing problems in the user. The funny thing is, it's the email that's the issue. I've tried
user = models.User( userId=1, firstName='Cameron', lastName='Jones', email='cejonesmsncom', phone='7192007970', passwordHash='60744474c3277428a8be861186e1e368', passwordSalt='s0mRIdlKvI', role='user', lastLogin=date.today(), joined=date.today())
and it throws ERROR: test_User (main.SubTest_Services)
Traceback (most recent call last): File "cameron_testing.py", line 68, in test_User joined=date.today()) File "", line 4, in init File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/state.py", line 196, in _initialize_instance return manager.original_init(_mixed[1:], *_kwargs) File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/ext/declarative/base.py", line 524, in _declarative_constructor setattr(self, k, kwargs[k]) File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/attributes.py", line 220, in set instance_dict(instance), value, None) File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/attributes.py", line 680, in set value, old, initiator) File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/attributes.py", line 698, in fire_replace_event value = fn(state, value, previous, initiator or self._replacetoken) File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/util.py", line 107, in set return validator(state.obj(), key, value)
TypeError: validate_type() takes exactly 2 arguments (3 given)
However, if I comment out the email, then it doesn't have a problem. I don't know why email would be a problem without other string values like firstName or role being issues as well. Comments?
Reply to this email directly or view it on GitHubhttps://github.com/byu-osl/city-issue-tracker/issues/1#issuecomment-37331119 .
Actually, I figured out what's going on
In your User class, you've got a validate thing for the email. It doesn't look finished however. I'll try and get that up and working.
Yeah, it wasn't finished yet. We needed to decide what kind of email addresses we were going to accept. It should be easy to implement a regex to validate them though, that was my plan. On Mar 11, 2014 12:44 PM, "Cameron Jones" notifications@github.com wrote:
Actually, I figured out what's going on
In your User class, you've got a validate thing for the email. It doesn't look finished however. I'll try and get that up and working.
Reply to this email directly or view it on GitHubhttps://github.com/byu-osl/city-issue-tracker/issues/1#issuecomment-37333942 .
I found this during a search: https://pypi.python.org/pypi/validate_email/1.1
Would something like this work?
It looks like it would work, Cameron, but it also may be a bit of overkill, as it looks like it also validates with the SMTP server at the email host to see if the email address exists. I don't know if we need to go that far or not.
Something like this would work for simple regex validation (with a regex like "[^@]+@[^@]+.[^@]+", but we should probably get a little more specific):
import re
EMAIL_REGEX = re.compile(r"<
On Tue, Mar 11, 2014 at 12:50 PM, Cameron Jones notifications@github.comwrote:
I found this during a search: https://pypi.python.org/pypi/validate_email/1.1
Would something like this work?
Reply to this email directly or view it on GitHubhttps://github.com/byu-osl/city-issue-tracker/issues/1#issuecomment-37334651 .
Would any of you guys know how to test for something that's supposed to fail? For instance, I'm wanting to test trying to put two users into the database that have the same userId. I get an error that I can't have two objects with the same userId in the database, which I want, but I want to write a test to confirm it. Is that possible?
Cameron
This what you want? http://stackoverflow.com/questions/129507/how-do-you-test-that-a-python-function-throws-an-exception
On Thu, Mar 13, 2014 at 12:36 PM, Cameron Jones notifications@github.comwrote:
Would any of you guys know how to test for something that's supposed to fail? For instance, I'm wanting to test trying to put two users into the database that have the same userId. I get an error that I can't have two objects with the same userId in the database, which I want, but I want to write a test to confirm it. Is that possible?
Cameron
Reply to this email directly or view it on GitHubhttps://github.com/byu-osl/city-issue-tracker/issues/1#issuecomment-37570452 .
That was the Stackoverflow question I was looking for.
Thanks.
Just a quick follow up to Sam's question about camel case and such. We're doing camel case all the way through the back end. Table names, column names, variable names. Class names are camel case with the first word capital.
Two things I want to discus,
First, there is a new model called CITModel
that other models can inherit. It helps with conversions to and eventually from JSON and XML. We will need certain functions (toJSON
and toXML
) in every model to satisfy the XML requirements of open311 and the JSON we want to mainly use for ourselves. In order to do this we need some way of converting each model into a simple dictionary and from there it is easy and generic to convert to JSON or XML and hence the CITModel
to simplify this is by inheritance.
In order for a model to inherit from CITModel
, it needs to override two attributes _open311Name
and _open311ListName
(see app/models/citmodel.py
for more of an explanation) and eventually two methods toDict
and fromDict
(fromDict
is not yet setup). toDict
just helps with the model to dictionary conversion and from there we can called the function toFormat
with a string of either "json"
or "xml"
and it will convert it for us. Ex. s.toFormat(format)
. To do lists, there is a class function which can be called. Ex. Service.composeFormatList(format, arr)
and it will convert a list of objects for us.
I started off the first point with the assumption that would want to use this but are their any reasons that we might not want to do this? Or anything that we should change?
Second, so we decided camelCase
however open311 is snake_case
. Do we want everything going out of the Open311 api to be underscored and everything out of our internal routes to be camel cased?
Blegh. I hate differences in cases and such. Why can't everyone just agree on a particular standard?! I don't have an opinion. We can go with underscored snake case so we're standard across the board. As for your other comment, I like it. I think it's a good idea to put some of the functionality in base classes and not have duplicated code.
Ok. If Open311 is snake_case, I guess I'm okay with changing everything around so it's all standard. Should we vote? I also agree with you guys about the base classes.
On Mon, Mar 17, 2014 at 12:09 PM, David Barley notifications@github.comwrote:
Blegh. I hate differences in cases and such. Why can't everyone just agree on a particular standard?! I don't have an opinion. We can go with underscored snake case so we're standard across the board. As for your other comment, I like it. I think it's a good idea to put some of the functionality in base classes and not have duplicated code.
Reply to this email directly or view it on GitHubhttps://github.com/byu-osl/city-issue-tracker/issues/1#issuecomment-37849957 .
That is going to screw up my test cases. I prefer not to, but who am I to argue against the will of the majority?
I think we can just go with snake_case in the toDict
function and fromDict
functions. We can internally(and by that I mean on the backend side) be camelCase and the front end team will just have to accept our snake_case since toJSON
uses our toDict
function. I just DO NOT want two toDict
functions to do either snake_case or camelCase. That is my preference.
I +1 what @kamn said. I feel like I'm a sheep just agreeing with everything, but I do actually agree.
Have you guys decided yet on what's going on with the servicerequest.py yet? I see a couple of TODOs on there.
While working on the error responding side of things and I have created a new function and a new Exception class to help us out.
I noticed that we needed a way to throw back errors in some easy json format. The open311 spec returns the following structure
[ { "code":403, "description":"Invalid api_key received -- can't proceed with create_request." } ]
In that same vein I created a function genError(code, string)
which populates something like the above(except not in an array). It is currently is just json and creates a response object with the correct status code and the message. I need to make it into XML some time also.
While I was generating the error code I was testing the ability of Service to detect an incorrect type attribute and I realize that we can get AssertionErrors
but that does not tell us anything about what is wrong about the input. For example if we have to validate two different attributes and one fails which was it and what is an easy message for the user to see?
To solve this problem a little I thought that a ValidationError
Exception class could help out. There is a simple example in Service
which basically makes the validate function check then throw a ValidationError
with a message.
ValidationError("This is wrong!")
This way we can know exactly what went wrong and display a nice error to the user.
I am just going to kick off the discussion for the Backend team(David, Mike and me) so everyone else can ignore this thread. I just wanted to mention what I am doing, an ideas and some questions.
First, I am going over a Flask tutorial which mentions some modules that might be useful. Once I am done I will give a quick overview.
Second, I think that we should try to get some simple open-source 311 api server running for the front-end team. To give them something to work with early. Like I have mentioned I think this might work but I am not sure. I will try to get that up and working after the Flask tutorial(or after reading the Open311 specs)
Third, the backend is going to be at least partially an API but is it only going to be an API? For example, are we going to allow a simple interface for viewing databases or managing issues(like closing them or reassigning them)? Or is that the front end's job? If we are going to be adding some interactive features, which? I feel like parts of this were discussed but I would like to see something in writing.
Fourth, I have not gotten to the Open311 spec yet but probably will by the end of the weekend but has anything stood out(important to note) to either of you guys?