Skip to content

Commit

Permalink
lint: use black and isort as backend standard (#12)
Browse files Browse the repository at this point in the history
- Added comand for backend linting to pr
template and read me
- linted everything. literally.
  • Loading branch information
gaoxk authored May 29, 2021
1 parent 2054b07 commit cd08a04
Show file tree
Hide file tree
Showing 30 changed files with 243 additions and 226 deletions.
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@
## Checklist
- [ ] My PR name is descriptive and in imperative tense
- [ ] My commit messages are descriptive and in imperative tense. My commits are atomic and trivial commits are squashed or fixup'd into non-trivial commits
- [ ] I have run the appropriate linter(s)
- [ ] For backend changes, I have run the appropriate linters: `docker exec -it <backend-container-id> /bin/bash -c "black . && isort --profile black ."`
- [ ] I have requested a review from the PL, as well as other devs who have background knowledge on this PR or who will be building on top of this PR
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ And run the project:
```
docker-compose up --build
```

Don't forget to seed your database with a firebase user:
```
docker ps
Expand All @@ -41,6 +42,12 @@ INSERT INTO users (first_name, last_name, auth_id, role) VALUES ('First', 'Last'

If there are no tables in the DB, go into `/backend/python/app/models/__init__.py` and change the `erase_db_and_sync = False` to True, allow the hot reload to build, and change it back to `False`. Try to seed the database with a user again.

# Lint
Frontend has on-save linting. To lint the backend:
```
docker exec -it <backend-container-id> /bin/bash -c "black . && isort --profile black ."
```

Follow the [getting started](https://uwblueprint.github.io/starter-code-v2/docs/getting-started) for more details, especially if you desire to use your own firebase and gcp projects.

Happy hacking! 💻🚀
5 changes: 3 additions & 2 deletions backend/python/app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import os
import firebase_admin
from logging.config import dictConfig

import firebase_admin
from flask import Flask
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from logging.config import dictConfig

from .config import app_config

Expand Down Expand Up @@ -56,6 +56,7 @@ def create_app(config_name):
rest.init_app(app)

from . import graphql

graphql.init_app(app)

return app
14 changes: 8 additions & 6 deletions backend/python/app/graphql/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
from flask import current_app
from flask_graphql import GraphQLView

from ..models import db
from .schema import schema
from ..services.implementations.entity_service import EntityService
from .schema import schema
from .service import services

from flask_graphql import GraphQLView
from flask import current_app

def init_app(app):
app.add_url_rule(
"/graphql", view_func=GraphQLView.as_view(
"graphql", schema=schema, graphiql=True,
context= {"session": db.session})
"/graphql",
view_func=GraphQLView.as_view(
"graphql", schema=schema, graphiql=True, context={"session": db.session}
),
)

services["entity"] = EntityService(current_app.logger)
5 changes: 2 additions & 3 deletions backend/python/app/graphql/mutations/auth_mutation.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'''
"""
TODO mutations:
login(email: String!, password: String!): AuthDTO!
refresh: String!
logout(userId: ID!): ID
resetPassword(email: String!): Boolean!
'''

"""
14 changes: 8 additions & 6 deletions backend/python/app/graphql/mutations/entity_mutation.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
from ..types.entity_type import EntityResponseDTO, EntityRequestDTO
import graphene

from ..service import services
from ..types.entity_type import EntityRequestDTO, EntityResponseDTO

import graphene

class CreateEntity(graphene.Mutation):
class Arguments:
entity_data = EntityRequestDTO(required=True)

ok = graphene.Boolean()
entity = graphene.Field(lambda: EntityResponseDTO)

def mutate(root, info, entity_data=None):
entity_response = services["entity"].create_entity(entity_data)
entity_response = services["entity"].create_entity(entity_data)
ok = True
return CreateEntity(entity=entity_response, ok=ok)

# optional TODO:

# optional TODO:
# updateEntity(id: ID!, entity: EntityRequestDTO!): EntityResponseDTO!
# deleteEntity(id: ID!): ID
# deleteEntity(id: ID!): ID
12 changes: 7 additions & 5 deletions backend/python/app/graphql/mutations/user_mutation.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from ..types.user_type import CreateUserDTO, UserDTO
import graphene

from ..service import services
from ..types.user_type import CreateUserDTO, UserDTO

import graphene

class CreateUser(graphene.Mutation):
class Arguments:
user_data = CreateUserDTO(required=True)

ok = graphene.Boolean()
user = graphene.Field(lambda: UserDTO)

Expand All @@ -15,10 +16,11 @@ def mutate(root, info, user_data=None):
ok = True
return CreateUser(user=user_response, ok=ok)

'''

"""
TODO mutations:
updateUser(id: ID!, user: UpdateUserDTO!): UserDTO!
deleteUserById(id: ID!): ID
deleteUserByEmail(email: String!): IDdeleteUserById
deleteUserByEmail
'''
"""
3 changes: 2 additions & 1 deletion backend/python/app/graphql/queries/entity_query.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from ..service import services


def resolve_entities(root, info, **kwargs):
return services['entity'].get_entities()
return services["entity"].get_entities()
11 changes: 5 additions & 6 deletions backend/python/app/graphql/schema.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from .types.entity_type import EntityResponseDTO
from .types.user_type import UserDTO
import graphene

from .mutations.entity_mutation import CreateEntity
from .mutations.user_mutation import CreateUser
from .service import services
from .queries.entity_query import resolve_entities
from .queries.user_query import resolve_users, resolve_user_by_id, resolve_user_by_email

import graphene
from .queries.user_query import resolve_user_by_email, resolve_user_by_id, resolve_users
from .types.entity_type import EntityResponseDTO
from .types.user_type import UserDTO


class Mutation(graphene.ObjectType):
Expand Down
6 changes: 3 additions & 3 deletions backend/python/app/graphql/service.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'''
"""
..services.implementations can only be leveraged once the
flask app has started. Create dummy services here that will be
updated with live app loggers during __init__.py
'''
"""
from ..services.implementations.entity_service import EntityService
from ..services.implementations.user_service import UserService

services = {
"entity": EntityService(),
"user": UserService(),
}
}
5 changes: 4 additions & 1 deletion backend/python/app/graphql/types/entity_type.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import graphene


class EntityEnum(graphene.Enum):
A = "A"
B = "B"
C = "C"
D = "D"


class EntityResponseDTO(graphene.ObjectType):
id = graphene.Int()
string_field = graphene.String(required=True)
Expand All @@ -14,9 +16,10 @@ class EntityResponseDTO(graphene.ObjectType):
enum_field = graphene.Field(EntityEnum, required=True)
bool_field = graphene.Boolean(required=True)


class EntityRequestDTO(graphene.InputObjectType):
string_field = graphene.String(required=True)
int_field = graphene.Int(required=True)
string_array_field = graphene.List(graphene.String, required=True)
enum_field = graphene.Argument(EntityEnum, required=True)
enum_field = graphene.Argument(EntityEnum, required=True)
bool_field = graphene.Boolean(required=True)
5 changes: 4 additions & 1 deletion backend/python/app/graphql/types/user_type.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import graphene
import graphene


class RoleEnum(graphene.Enum):
User = "User"
Admin = "Admin"


class UserDTO(graphene.ObjectType):
id = graphene.Int()
first_name = graphene.String(required=True)
last_name = graphene.String(required=True)
role = graphene.Field(RoleEnum, required=True)
email = graphene.String(required=True)


class CreateUserDTO(graphene.InputObjectType):
first_name = graphene.String(required=True)
last_name = graphene.String(required=True)
Expand Down
3 changes: 2 additions & 1 deletion backend/python/app/middlewares/auth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from flask import current_app, jsonify, request
from functools import wraps

from flask import current_app, jsonify, request

from ..services.implementations.auth_service import AuthService
from ..services.implementations.user_service import UserService

Expand Down
4 changes: 2 additions & 2 deletions backend/python/app/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

def init_app(app):
from .entity import Entity
from .user import User
from .story import Story
from .file import File
from .story import Story
from .user import User

app.app_context().push()
db.init_app(app)
Expand Down
91 changes: 45 additions & 46 deletions backend/python/app/models/entity.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,45 @@
from sqlalchemy import inspect
from sqlalchemy.orm.properties import ColumnProperty

from . import db
from .enum import enum

# common columns and methods across multiple data models can be added via a Mixin class:
# https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/mixins.html

# see examples of Mixins in current and past Blueprint projects:
# https://github.com/uwblueprint/dancefest-web/blob/master/db/models.py#L10-L70
# https://github.com/uwblueprint/plasta/blob/master/backend/app/models/mixins.py#L10-L95


class Entity(db.Model):
# define the entities table

__tablename__ = "entities"

id = db.Column(db.Integer, primary_key=True, nullable=False)
string_field = db.Column(db.String, nullable=False)
int_field = db.Column(db.Integer, nullable=False)
enum_field = db.Column(enum, nullable=False)
string_array_field = db.Column(db.ARRAY(db.String), nullable=False)
bool_field = db.Column(db.Boolean, nullable=False)
# must define how to convert to a dict so that Entity can eventually be serialized into JSON
# this would be a good method to include in a base Mixin
def to_dict(self, include_relationships=False):
cls = type(self)
# mapper allows us to grab the columns of a Model
mapper = inspect(cls)
formatted = {}
for column in mapper.attrs:
field = column.key
attr = getattr(self, field)
# if it's a regular column, extract the value
if isinstance(column, ColumnProperty):
formatted[field] = attr
# otherwise, it's a relationship field
# (currently not applicable, but may be useful for entity groups)
elif include_relationships:
# recursively format the relationship
# don't format the relationship's relationships
formatted[field] = [obj.to_dict() for obj in attr]
return formatted

from sqlalchemy import inspect
from sqlalchemy.orm.properties import ColumnProperty

from . import db
from .enum import enum

# common columns and methods across multiple data models can be added via a Mixin class:
# https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/mixins.html

# see examples of Mixins in current and past Blueprint projects:
# https://github.com/uwblueprint/dancefest-web/blob/master/db/models.py#L10-L70
# https://github.com/uwblueprint/plasta/blob/master/backend/app/models/mixins.py#L10-L95


class Entity(db.Model):
# define the entities table

__tablename__ = "entities"

id = db.Column(db.Integer, primary_key=True, nullable=False)
string_field = db.Column(db.String, nullable=False)
int_field = db.Column(db.Integer, nullable=False)
enum_field = db.Column(enum, nullable=False)
string_array_field = db.Column(db.ARRAY(db.String), nullable=False)
bool_field = db.Column(db.Boolean, nullable=False)
# must define how to convert to a dict so that Entity can eventually be serialized into JSON
# this would be a good method to include in a base Mixin
def to_dict(self, include_relationships=False):
cls = type(self)
# mapper allows us to grab the columns of a Model
mapper = inspect(cls)
formatted = {}
for column in mapper.attrs:
field = column.key
attr = getattr(self, field)
# if it's a regular column, extract the value
if isinstance(column, ColumnProperty):
formatted[field] = attr
# otherwise, it's a relationship field
# (currently not applicable, but may be useful for entity groups)
elif include_relationships:
# recursively format the relationship
# don't format the relationship's relationships
formatted[field] = [obj.to_dict() for obj in attr]
return formatted
3 changes: 2 additions & 1 deletion backend/python/app/models/story.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

from . import db

stages_enum = db.Enum("START", "TRANSLATE", "REVIEW", "PUBLISH", name="stages" )
stages_enum = db.Enum("START", "TRANSLATE", "REVIEW", "PUBLISH", name="stages")


class Story(db.Model):
__tablename__ = "stories"
Expand Down
Loading

0 comments on commit cd08a04

Please sign in to comment.