Create eTags, lots, and events in dummy

This commit is contained in:
Xavier Bustamante Talavera 2018-09-20 16:40:41 +02:00
parent 32837f5f59
commit f56ca473e8
9 changed files with 114 additions and 28 deletions

View File

@ -1,4 +1,7 @@
import itertools
import json
from pathlib import Path
from typing import Set
import click
import click_spinner
@ -7,22 +10,26 @@ import yaml
from ereuse_devicehub.client import UserClient
from ereuse_devicehub.db import db
from ereuse_devicehub.resources.agent.models import Person
from ereuse_devicehub.resources.event.models import Snapshot
from ereuse_devicehub.resources.event import models as m
from ereuse_devicehub.resources.inventory import Inventory
from ereuse_devicehub.resources.tag.model import Tag
from ereuse_devicehub.resources.user import User
class Dummy:
SNAPSHOTS = (
'workbench-server-1',
'computer-monitor'
)
TAGS = (
'tag1',
'tag2',
'tag3'
)
"""Tags to create."""
ET = (
('A0000000000001', 'DT-AAAAA'),
('A0000000000002', 'DT-BBBBB'),
)
"""eTags to create."""
ORG = 'eReuse.org CAT', 'G-60437761', 'ES'
"""An organization to create."""
def __init__(self, app) -> None:
super().__init__()
@ -34,20 +41,60 @@ class Dummy:
'Do you want to continue?')
def run(self):
print('Preparing the database...'.ljust(30), end='')
runner = self.app.test_cli_runner()
with click_spinner.spinner():
self.app.init_db(erase=True)
out = runner.invoke(args=['create-org', *self.ORG], catch_exceptions=False).output
org_id = json.loads(out)['id']
user = self.user_client('user@dhub.com', '1234')
# todo put user's agent into Org
for id in self.TAGS:
user.post({'id': id}, res=Tag)
for id, sec in self.ET:
runner.invoke(args=[
'create-tag', id,
'-p', 'https://t.devicetag.io',
'-s', sec,
'-o', org_id
],
catch_exceptions=False)
files = tuple(Path(__file__).parent.joinpath('files').iterdir())
print('done.')
pcs = set() # type: Set[int]
with click.progressbar(files, label='Creating devices...'.ljust(28)) as bar:
for path in bar:
with path.open() as f:
snapshot = yaml.load(f)
user.post(res=Snapshot, data=snapshot)
s, _ = user.post(res=m.Snapshot, data=snapshot)
pcs.add(s['device']['id'])
inventory, _ = user.get(res=Inventory)
assert len(inventory['devices'])
# Link tags and eTags
for tag, pc in zip((self.TAGS[1], self.TAGS[2], self.ET[0][0], self.ET[1][1]), pcs):
user.put({}, res=Tag, item='{}/device/{}'.format(tag, pc), status=204)
# Perform generic events
for pc, model in zip(pcs,
{m.ToRepair, m.Repair, m.ToPrepare, m.ReadyToUse, m.ToPrepare,
m.Prepare}):
user.post({'type': model.t, 'devices': [pc]}, res=m.Event)
# Perform a Sell to several devices
user.post(
{
'type': m.Sell.t,
'to': user.user['individuals'][0]['id'],
'devices': list(itertools.islice(pcs, len(pcs) // 2))
},
res=m.Event)
from tests.test_lot import test_post_add_children_view, test_post_add_device_view
test_post_add_children_view(user)
# todo this does not add devices to lots
test_post_add_device_view(user)
print('⭐ Done.')
def user_client(self, email: str, password: str):

View File

@ -1,3 +1,5 @@
import json
import click
from flask import current_app as app
from teal.db import SQLAlchemy
@ -39,7 +41,9 @@ class OrganizationDef(AgentDef):
))
db.session.add(org)
db.session.commit()
return self.schema.dump(org)
o = self.schema.dump(org)
print(json.dumps(o, indent=2))
return o
def init_db(self, db: SQLAlchemy):
"""Creates the default organization."""

View File

@ -43,11 +43,6 @@ class Agent(Thing):
telephone = Column(PhoneNumberType())
email = Column(EmailType, unique=True)
user_id = Column(UUID(as_uuid=True), ForeignKey(User.id), unique=True)
user = relationship(User,
backref=backref('individuals', lazy=True, collection_class=set),
primaryjoin=user_id == User.id)
__table_args__ = (
UniqueConstraint(tax_id, country, name='Registration Number per country.'),
)
@ -100,6 +95,11 @@ class Individual(JoinedTableMixin, Agent):
active_org_id = Column(UUID(as_uuid=True), ForeignKey(Organization.id))
active_org = relationship(Organization, primaryjoin=active_org_id == Organization.id)
user_id = Column(UUID(as_uuid=True), ForeignKey(User.id), unique=True)
user = relationship(User,
backref=backref('individuals', lazy=True, collection_class=set),
primaryjoin=user_id == User.id)
class Membership(Thing):
"""Organizations that are related to the Individual.

View File

@ -727,10 +727,7 @@ class Trade(JoinedTableMixin, EventWithMultipleDevices):
If no price is set it is supposed that the trade was
not payed, usual in donations.
"""
to_id = Column(UUID(as_uuid=True),
ForeignKey(Agent.id),
nullable=False,
default=lambda: g.user.id)
to_id = Column(UUID(as_uuid=True), ForeignKey(Agent.id), nullable=False)
# todo compute the org
to = relationship(Agent,
backref=backref('events_to',
@ -738,6 +735,9 @@ class Trade(JoinedTableMixin, EventWithMultipleDevices):
collection_class=OrderedSet,
order_by=lambda: Event.created),
primaryjoin=to_id == Agent.id)
to_comment = """
The agent that gets the device due this deal.
"""
confirms_id = Column(UUID(as_uuid=True), ForeignKey(Organize.id))
confirms = relationship(Organize,
backref=backref('confirmation', lazy=True, uselist=False),

View File

@ -352,7 +352,7 @@ class Trade(EventWithMultipleDevices):
shipping_date = DateTime(data_key='shippingDate')
invoice_number = String(validate=Length(max=STR_SIZE), data_key='invoiceNumber')
price = NestedOn(Price)
to = NestedOn(Agent, only_query='id')
to = NestedOn(Agent, only_query='id', required=True, comment=m.Trade.to_comment)
confirms = NestedOn(Organize)

View File

@ -26,9 +26,10 @@ class Tag(Thing):
primaryjoin=Organization.id == org_id,
collection_class=set)
"""The organization that issued the tag."""
provider = Column(URL(),
comment='The tag provider URL. If None, the provider is this Devicehub.')
provider.comment = """The provider URL."""
provider = Column(URL())
provider.comment = """
The tag provider URL. If None, the provider is this Devicehub.
"""
device_id = Column(BigInteger,
# We don't want to delete the tag on device deletion, only set to null
ForeignKey(Device.id, ondelete=DB_CASCADE_SET_NULL))
@ -68,6 +69,13 @@ class Tag(Thing):
raise ValidationError('Tags cannot contain slashes (/).')
return value
@validates('provider')
def use_only_domain(self, _, url: URL):
if url.path:
raise ValidationError('Provider can only contain scheme and host',
field_names=['provider'])
return url
__table_args__ = (
UniqueConstraint(device_id, org_id, name='one_tag_per_org'),
UniqueConstraint(secondary, org_id, name='one_secondary_per_org')

View File

@ -1,7 +1,9 @@
from marshmallow.fields import String
from sqlalchemy.util import OrderedSet
from teal.marshmallow import URL
from ereuse_devicehub.marshmallow import NestedOn
from ereuse_devicehub.resources.agent.schemas import Organization
from ereuse_devicehub.resources.device.schemas import Device
from ereuse_devicehub.resources.schemas import Thing
from ereuse_devicehub.resources.tag import model as m
@ -19,5 +21,5 @@ class Tag(Thing):
provider = URL(description=m.Tag.provider.comment,
validator=without_slash)
device = NestedOn(Device, dump_only=True)
org = String()
org = NestedOn(Organization, collection_class=OrderedSet, only_query='id')
secondary = String(description=m.Tag.secondary.comment)

View File

@ -1,4 +1,5 @@
from click import argument, option
from flask import current_app
from teal.resource import Converters, Resource
from ereuse_devicehub.db import db
@ -22,14 +23,26 @@ class UserDef(Resource):
self.add_url_rule('/login', view_func=login, methods={'POST'})
@argument('email')
@option('--password', prompt=True, hide_input=True, confirmation_prompt=True)
def create_user(self, email: str, password: str) -> dict:
"""
Creates an user.
@option('-a', '--agent', help='The name of an agent to create with the user.')
@option('-c', '--country', help='The country of the agent (if --agent is set).')
@option('-t', '--telephone', help='The telephone of the agent (if --agent is set).')
@option('-t', '--tax-id', help='The tax id of the agent (if --agent is set).')
@option('-p', '--password', prompt=True, hide_input=True, confirmation_prompt=True)
def create_user(self, email: str, password: str, agent: str = None, country: str = None,
telephone: str = None, tax_id: str = None) -> dict:
"""Creates an user.
If ``--agent`` is passed, it creates an ``Individual`` agent
that represents the user.
"""
from ereuse_devicehub.resources.agent.models import Individual
u = self.SCHEMA(only={'email', 'password'}, exclude=('token',)) \
.load({'email': email, 'password': password})
user = User(**u)
agent = Individual(**current_app.resources[Individual.t].schema.load(
dict(name=agent, email=email, country=country, telephone=telephone, taxId=tax_id)
))
user.individuals.add(agent)
db.session.add(user)
db.session.commit()
return self.schema.dump(user)

View File

@ -3,6 +3,8 @@ from uuid import UUID
import pytest
from sqlalchemy_utils import Password
from teal.enums import Country
from teal.marshmallow import ValidationError
from werkzeug.exceptions import NotFound
from ereuse_devicehub.client import Client
@ -11,23 +13,33 @@ from ereuse_devicehub.devicehub import Devicehub
from ereuse_devicehub.resources.user import UserDef
from ereuse_devicehub.resources.user.exceptions import WrongCredentials
from ereuse_devicehub.resources.user.models import User
from teal.marshmallow import ValidationError
from tests.conftest import app_context, create_user
@pytest.mark.usefixtures(app_context.__name__)
def test_create_user_method(app: Devicehub):
def test_create_user_method_with_agent(app: Devicehub):
"""
Tests creating an user through the main method.
This method checks that the token is correct, too.
"""
user_def = app.resources['User'] # type: UserDef
u = user_def.create_user(email='foo@foo.com', password='foo')
u = user_def.create_user(email='foo@foo.com',
password='foo',
agent='Nice Person',
country=Country.ES.name,
telephone='+34 666 66 66 66',
tax_id='1234')
user = User.query.filter_by(id=u['id']).one() # type: User
assert user.email == 'foo@foo.com'
assert isinstance(user.token, UUID)
assert User.query.filter_by(email='foo@foo.com').one() == user
individual = next(iter(user.individuals))
assert individual.name == 'Nice Person'
assert individual.tax_id == '1234'
assert individual.telephone.e164 == '+34666666666'
assert individual.country == Country.ES
assert individual.email == user.email
@pytest.mark.usefixtures(app_context.__name__)