Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/professional' into professional
Browse files Browse the repository at this point in the history
  • Loading branch information
nbgitpuller committed Jun 30, 2020
2 parents 560be5f + 2f0686f commit c05c54d
Show file tree
Hide file tree
Showing 235 changed files with 14,252 additions and 0 deletions.
3 changes: 3 additions & 0 deletions 021-conical-continued-again/magnum_opus/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.egg-info
.tox
Pipfile
24 changes: 24 additions & 0 deletions 021-conical-continued-again/magnum_opus/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM python:3-alpine

RUN addgroup -S user && adduser user -S -G user

WORKDIR /home/user/

COPY requirements.txt .
COPY gunicorn_config.py .
COPY setup.py .
COPY setup.cfg .
COPY init_entrypoint.sh /

RUN pip install gunicorn
RUN pip install -r requirements.txt

USER user

EXPOSE 5000

ENTRYPOINT []

CMD gunicorn --config ./gunicorn_config.py magnumopus.index:app

COPY magnumopus magnumopus
1 change: 1 addition & 0 deletions 021-conical-continued-again/magnum_opus/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

5 changes: 5 additions & 0 deletions 021-conical-continued-again/magnum_opus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Magnum Opus
===========

This recipe maker
and averaged over repeated attempts, returning an average round trip time.
9 changes: 9 additions & 0 deletions 021-conical-continued-again/magnum_opus/RULES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
* One unit of each of the substances Mercury, Salt and Sulphur are mixed, using my "Alembic" (mixing pot), giving one unit of another substance, Gloop
* Any attempt to mix anything other than those three substances, gives Sludge, another substance
* Substances can undergo several Processes in my Alembic - they can be Cooked, Washed, Pickled or Fermented
* If Gloop is Cooked, Washed, Pickled and Fermented, in that order, it is the Philosopher's Stone (panacea and cure of all ills)
[* To process a Substance, at least one unit must be in my Pantry, including Gloop - even when freshly processed/created, it must be stored there before re-use (to cool)]

Final rule:
GROUP 1: When I process a substance, using any process, it becomes a different substance
GROUP 2: When I process a substance, its state changes but is essentially the same substance (NB: mixing is not a process)
10 changes: 10 additions & 0 deletions 021-conical-continued-again/magnum_opus/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: "3"
services:
web:
build: .
environment:
DATABASE_URI: 'sqlite:////docker/storage/storage.db'
volumes:
- ./docker:/docker
ports:
- 5000:5000
Binary file not shown.
5 changes: 5 additions & 0 deletions 021-conical-continued-again/magnum_opus/gunicorn_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
port = '5000'
bind = "0.0.0.0:%s" % port
workers = 1
timeout = 600
reload = False
6 changes: 6 additions & 0 deletions 021-conical-continued-again/magnum_opus/init_containers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh

mkdir -p docker/storage

docker-compose run --user root web /init_entrypoint.sh
docker-compose run web python3 -m magnumopus.initialize
3 changes: 3 additions & 0 deletions 021-conical-continued-again/magnum_opus/init_entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

chown -R user:user /docker/storage
17 changes: 17 additions & 0 deletions 021-conical-continued-again/magnum_opus/magnumopus/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from flask import Flask

def create_app():
from . import models, resources, schemas, logger

app = Flask(__name__)

# This could be used to separate by environment
app.config.from_object('magnumopus.config.Config')

# This helps avoid cyclic dependencies
logger.init_app(app)
models.init_app(app)
resources.init_app(app)
schemas.init_app(app)

return app
5 changes: 5 additions & 0 deletions 021-conical-continued-again/magnum_opus/magnumopus/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import os

class Config:
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URI', 'sqlite:////tmp/test.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
3 changes: 3 additions & 0 deletions 021-conical-continued-again/magnum_opus/magnumopus/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import create_app

app = create_app()
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from . import create_app
from .models import db

if __name__ == "__main__":
# There are nicer ways around this, but this keeps it clear for an example
app = create_app()

with app.app_context():
db.create_all()
5 changes: 5 additions & 0 deletions 021-conical-continued-again/magnum_opus/magnumopus/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import logging

def init_app(app):
app.logger.addHandler(logging.StreamHandler())
app.logger.setLevel(logging.INFO)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .base import db

def init_app(app):
db.init_app(app)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from . import db
from sqlalchemy_utils.types.scalar_list import ScalarListType

class SubstanceMustBeFreshToProcessException(Exception):
pass

class Substance(db.Model):
__tablename__ = 'substances'

id = db.Column(db.Integer, primary_key=True)
nature = db.Column(db.String(32), default='Unknown')
state = db.Column(ScalarListType())

def __init__(self, nature='Unknown'):
self.state = []
self.nature = nature

super(Substance, self).__init__()

def _process(self, process_name):
self.state.append(process_name)
return self

def cook(self):
return self._process('cooked')

def pickle(self):
return self._process('pickled')

def ferment(self):
return self._process('fermented')

def wash(self):
return self._process('washed')
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Pantry:
_cupboard = []

def __init__(self):
pass

def add_substance(self, substance):
self._cupboard.append(substance)

def find_substances_by_nature(self, nature):
return [substance for substance in self._cupboard if substance.nature == nature]

def count_all_substances(self):
return len(self._cupboard)

def commit(self):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from ..models.substance import Substance
from ..models import db

class SQLAlchemyPantry:
def __init__(self):
self._db = db

# A more involved solution would open and close the pantry... like
# a cupboard door, or a Unit of Work

# Note how we're committing too frequently?
def add_substance(self, substance):
self._db.session.add(substance)
return substance

def find_substances_by_nature(self, nature):
substances = Substance.query.filter_by(nature=nature).all()
return substances

def count_all_substances(self):
return Substance.count()

def commit(self):
self._db.session.commit()
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from flask_restful import Api
from . import substance
from . import alembic_instruction

def init_app(app):
api = Api(app)
substance.init_app(app, api)
alembic_instruction.init_app(app, api)
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from flask_restful import Resource, reqparse
from ..repositories.pantry import Pantry
from ..repositories.sqlalchemy_pantry import SQLAlchemyPantry
from ..models.substance import Substance
from ..schemas.substance_schema import SubstanceSchema
from ..services.alembic_instruction_handler import AlembicInstructionHandler, AlembicInstruction

parser = reqparse.RequestParser()
parser.add_argument('instruction_type')
parser.add_argument('action')
parser.add_argument('natures')

substance_schema = SubstanceSchema()

class AlembicInstructionResource(Resource):
def get(self):
"""This should return past requests/commands."""
pass

def post(self):
"""
Add an instruction for the alembic.
Note that POST is _not_ assumed to be idempotent, unlike PUT
"""

args = parser.parse_args()
instruction_type = args['instruction_type']

pantry = SQLAlchemyPantry()

instruction_handler = AlembicInstructionHandler()

# This could do with deserialization...
instruction = AlembicInstruction(
instruction_type=args.instruction_type,
natures=args.natures.split(','),
action=args.action
)

# Crude start at DI... see flask-injector
result = instruction_handler.handle(instruction, pantry)

pantry.add_substance(result)

pantry.commit()

return substance_schema.dump(result)


def init_app(app, api):
api.add_resource(AlembicInstructionResource, '/alembic_instruction')
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from flask_restful import Resource, reqparse
from ..repositories.pantry import Pantry
from ..repositories.sqlalchemy_pantry import SQLAlchemyPantry
from ..models import db
from ..models.substance import Substance
from ..schemas.substance_schema import SubstanceSchema

parser = reqparse.RequestParser()
parser.add_argument('nature')

substance_schema = SubstanceSchema()
substances_schema = SubstanceSchema(many=True)

pantry_cls = SQLAlchemyPantry


class SubstanceResource(Resource):
def get(self):
args = parser.parse_args()
nature = args['nature']

pantry = pantry_cls()

substances = pantry.find_substances_by_nature(nature)

return substances_schema.dump(substances)

def post(self):
args = parser.parse_args()
nature = args['nature']

pantry = pantry_cls()

substance = Substance(nature=nature)

pantry.add_substance(substance)

pantry.commit()

return substance_schema.dump(substance)


def init_app(app, api):
api.add_resource(SubstanceResource, '/substance')
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from flask_marshmallow import Marshmallow

ma = Marshmallow()

def init_app(app):
ma.init_app(app)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from marshmallow import fields

from . import ma

from ..models.substance import Substance
from ..services.assessor import assess_whether_substance_is_philosophers_stone

class SubstanceSchema(ma.SQLAlchemySchema):
is_philosophers_stone = fields.Function(
assess_whether_substance_is_philosophers_stone
)

class Meta:
model = Substance
fields = ('id', 'nature', 'is_philosophers_stone', 'state')

id = ma.auto_field()
nature = ma.auto_field()
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from ..models.substance import Substance

class NotEnoughSubstancesToMixException(Exception):
pass

class UnknownProcessException(Exception):
pass


MIXTURES = {
('Mercury', 'Salt', 'Sulphur'): 'Gloop'
}


class Alembic:
_nature_of_unknown_mixture = 'Sludge'

@staticmethod
def _produce(nature):
return Substance(nature=nature)

def mix(self, *substances):
if len(substances) < 2:
raise NotEnoughSubstancesToMixException()

constituents = [substance.nature for substance in substances]

# This gives us a canonical, ordered way of expressing our
# constituents that we can use as a recipe look-up
ingredient_list = tuple(sorted(constituents))

try:
nature = MIXTURES[ingredient_list]
except KeyError:
nature = self._nature_of_unknown_mixture

return self._produce(nature)

def process(self, process_name, substance):
if process_name == 'ferment':
result = substance.ferment()
elif process_name == 'cook':
result = substance.cook()
elif process_name == 'wash':
result = substance.wash()
elif process_name == 'pickle':
result = substance.pickle()
else:
raise UnknownProcessException()

return result
Loading

0 comments on commit c05c54d

Please sign in to comment.