Add Manufacturer

This commit is contained in:
Xavier Bustamante Talavera 2018-09-30 19:40:28 +02:00
parent 042b7718ec
commit 38060d47ec
9 changed files with 2952 additions and 6 deletions

View File

@ -23,6 +23,9 @@ an ID and a tag provider. Note though that these virtual tags don't have
to forcefully be printed or have a physical representation to forcefully be printed or have a physical representation
(this is not imposed at system level). (this is not imposed at system level).
Tags are case insensitive and are converted to lower-case in
Devicehub.
eTags eTags
***** *****
We recognize a special type of tag, the **eReuse.org tags (eTag)**. We recognize a special type of tag, the **eReuse.org tags (eTag)**.

View File

@ -1,7 +1,8 @@
from teal.resource import Converters, Resource from teal.resource import Converters, Resource
from ereuse_devicehub.resources.device import schemas from ereuse_devicehub.resources.device import schemas
from ereuse_devicehub.resources.device.views import DeviceView from ereuse_devicehub.resources.device.models import Manufacturer
from ereuse_devicehub.resources.device.views import DeviceView, ManufacturerView
class DeviceDef(Resource): class DeviceDef(Resource):
@ -119,3 +120,13 @@ class SoundCardDef(ComponentDef):
class DisplayDef(ComponentDef): class DisplayDef(ComponentDef):
VIEW = None VIEW = None
SCHEMA = schemas.Display SCHEMA = schemas.Display
class ManufacturerDef(Resource):
VIEW = ManufacturerView
SCHEMA = schemas.Manufacturer
AUTH = True
def init_db(self, db: 'db.SQLAlchemy'):
"""Loads the manufacturers to the database."""
Manufacturer.add_all_to_session(db.session)

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,12 @@
import json
import pathlib
from contextlib import suppress from contextlib import suppress
from itertools import chain from itertools import chain
from operator import attrgetter from operator import attrgetter
from typing import Dict, Set from typing import Dict, Set
from boltons import urlutils
from citext import CIText
from ereuse_utils.naming import Naming from ereuse_utils.naming import Naming
from sqlalchemy import BigInteger, Boolean, Column, Enum as DBEnum, Float, ForeignKey, Integer, \ from sqlalchemy import BigInteger, Boolean, Column, Enum as DBEnum, Float, ForeignKey, Integer, \
Sequence, SmallInteger, Unicode, inspect Sequence, SmallInteger, Unicode, inspect
@ -11,10 +15,11 @@ from sqlalchemy.orm import ColumnProperty, backref, relationship, validates
from sqlalchemy.util import OrderedSet from sqlalchemy.util import OrderedSet
from sqlalchemy_utils import ColorType from sqlalchemy_utils import ColorType
from stdnum import imei, meid from stdnum import imei, meid
from teal.db import CASCADE, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, check_lower, \ from teal.db import CASCADE, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, URL, check_lower, \
check_range check_range
from teal.marshmallow import ValidationError from teal.marshmallow import ValidationError
from ereuse_devicehub.db import db
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \ from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \
RamFormat, RamInterface RamFormat, RamInterface
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
@ -316,3 +321,25 @@ class Display(JoinedComponentTableMixin, DisplayMixin, Component):
and Television Set. and Television Set.
""" """
pass pass
class Manufacturer(db.Model):
__table_args__ = {'schema': 'common'}
CUSTOM_MANUFACTURERS = {'Belinea', 'OKI Data Corporation', 'Vivitek', 'Yuraku'}
"""A list of manufacturer names that are not from Wikipedia's JSON."""
name = db.Column(CIText(), primary_key=True)
url = db.Column(URL(), unique=True)
logo = db.Column(URL())
@classmethod
def add_all_to_session(cls, session):
"""Adds all manufacturers to session."""
with pathlib.Path(__file__).parent.joinpath('manufacturers.json').open() as f:
for m in json.load(f):
man = cls(name=m['name'],
url=urlutils.URL(m['url']),
logo=urlutils.URL(m['logo']) if m.get('logo', None) else None)
session.add(man)
for name in cls.CUSTOM_MANUFACTURERS:
session.add(cls(name=name))

View File

@ -1,8 +1,10 @@
from typing import Dict, List, Set from typing import Dict, List, Set
from boltons.urlutils import URL
from colour import Color from colour import Color
from sqlalchemy import Column, Integer from sqlalchemy import Column, Integer
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from teal.db import Model
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \ from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \
RamFormat, RamInterface RamFormat, RamInterface
@ -210,3 +212,20 @@ class RamModule(Component):
class Display(DisplayMixin, Component): class Display(DisplayMixin, Component):
pass pass
class Manufacturer(Model):
CUSTOM_MANUFACTURERS = ... # type: set
name = ... # type: Column
url = ... # type: Column
logo = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__()
self.name = ... # type: str
self.url = ... # type: URL
self.logo = ... # type: URL
@classmethod
def add_all_to_session(cls, session):
pass

View File

@ -1,9 +1,10 @@
from marshmallow import post_load, pre_load from marshmallow import post_load, pre_load
from marshmallow.fields import Boolean, Float, Integer, Str from marshmallow.fields import Boolean, Float, Integer, Str, String
from marshmallow.validate import Length, OneOf, Range from marshmallow.validate import Length, OneOf, Range
from sqlalchemy.util import OrderedSet from sqlalchemy.util import OrderedSet
from stdnum import imei, meid from stdnum import imei, meid
from teal.marshmallow import EnumField, SanitizedStr, ValidationError from teal.marshmallow import EnumField, SanitizedStr, URL, ValidationError
from teal.resource import Schema
from ereuse_devicehub.marshmallow import NestedOn from ereuse_devicehub.marshmallow import NestedOn
from ereuse_devicehub.resources.device import models as m from ereuse_devicehub.resources.device import models as m
@ -183,3 +184,9 @@ class SoundCard(Component):
class Display(DisplayMixin, Component): class Display(DisplayMixin, Component):
pass pass
class Manufacturer(Schema):
name = String(dump_only=True)
url = URL(dump_only=True)
logo = URL(dump_only=True)

View File

@ -1,6 +1,10 @@
import marshmallow
from flask import current_app as app
from flask.json import jsonify
from flask_sqlalchemy import Pagination
from teal.resource import View from teal.resource import View
from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.device.models import Device, Manufacturer
class DeviceView(View): class DeviceView(View):
@ -29,3 +33,23 @@ class DeviceView(View):
def find(self, args: dict): def find(self, args: dict):
"""Gets many devices.""" """Gets many devices."""
return self.schema.jsonify(Device.query, many=True) return self.schema.jsonify(Device.query, many=True)
class ManufacturerView(View):
class FindArgs(marshmallow.Schema):
name = marshmallow.fields.Str(required=True,
# Disallow like operators
validate=lambda x: '%' not in x and '_' not in x)
def find(self, args: dict):
name = args['name']
manufacturers = Manufacturer.query \
.filter(Manufacturer.name.ilike(name + '%')) \
.paginate(page=1, per_page=6) # type: Pagination
return jsonify(
items=app.resources[Manufacturer.t].schema.dump(
manufacturers.items,
many=True,
nested=1
)
)

View File

@ -26,6 +26,7 @@ def test_api_docs(client: Client):
'/users/login', '/users/login',
'/events/', '/events/',
'/lots/', '/lots/',
'/manufacturers/',
'/lots/{id}/children', '/lots/{id}/children',
'/lots/{id}/devices', '/lots/{id}/devices',
'/tags/{tag_id}/device/{device_id}' '/tags/{tag_id}/device/{device_id}'
@ -39,4 +40,4 @@ def test_api_docs(client: Client):
'scheme': 'basic', 'scheme': 'basic',
'name': 'Authorization' 'name': 'Authorization'
} }
assert 77 == len(docs['definitions']) assert 78 == len(docs['definitions'])

View File

@ -467,3 +467,14 @@ def test_device_search_all_devices_token_if_empty(app: Devicehub, user: UserClie
DeviceSearch.set_all_devices_tokens_if_empty(app.db.session) DeviceSearch.set_all_devices_tokens_if_empty(app.db.session)
i, _ = user.get(res=Inventory, query=[('search', 'Desktop')]) i, _ = user.get(res=Inventory, query=[('search', 'Desktop')])
assert not len(i['devices']) assert not len(i['devices'])
def test_manufacturer(user: UserClient):
m, _ = user.get(res='Manufacturer', query=[('name', 'asus')])
assert m == {'items': [{'name': 'Asus', 'url': 'https://en.wikipedia.org/wiki/Asus'}]}
@pytest.mark.xfail(reason='Develop functionality')
def test_manufacturer_enforced():
"""Ensures that non-computer devices can submit only
manufacturers from the Manufacturer table."""