Source code for camcops_server.cc_modules.cc_device

#!/usr/bin/env python
# camcops_server/cc_modules/cc_device.py

"""
===============================================================================

    Copyright (C) 2012-2018 Rudolf Cardinal (rudolf@pobox.com).

    This file is part of CamCOPS.

    CamCOPS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CamCOPS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CamCOPS. If not, see <http://www.gnu.org/licenses/>.

===============================================================================
"""

from typing import Optional, TYPE_CHECKING

from cardinal_pythonlib.classes import classproperty
from pendulum import DateTime as Pendulum
from sqlalchemy.orm import Query, relationship, Session as SqlASession
from sqlalchemy.sql.schema import Column, ForeignKey
from sqlalchemy.sql.sqltypes import Boolean, DateTime, Integer, Text

from .cc_constants import DEVICE_NAME_FOR_SERVER
from .cc_report import Report
from .cc_unittest import DemoDatabaseTestCase
from .cc_user import User
from .cc_sqla_coltypes import (
    DeviceNameColType,
    SemanticVersionColType,
)
from .cc_sqlalchemy import Base
from .cc_version import CAMCOPS_SERVER_VERSION

if TYPE_CHECKING:
    from .cc_request import CamcopsRequest


# =============================================================================
# Device class
# =============================================================================

[docs]class Device(Base): """Represents a tablet device.""" __tablename__ = "_security_devices" id = Column( "id", Integer, primary_key=True, autoincrement=True, comment="ID of the source tablet device" ) name = Column( "name", DeviceNameColType, unique=True, index=True, comment="Short cryptic unique name of the source tablet device" ) registered_by_user_id = Column( "registered_by_user_id", Integer, ForeignKey("_security_users.id"), comment="ID of user that registered the device" ) registered_by_user = relationship("User", foreign_keys=[registered_by_user_id]) when_registered_utc = Column( "when_registered_utc", DateTime, comment="Date/time when the device was registered (UTC)" ) friendly_name = Column( "friendly_name", Text, comment="Friendly name of the device" ) camcops_version = Column( "camcops_version", SemanticVersionColType, comment="CamCOPS version number on the tablet device" ) last_upload_batch_utc = Column( "last_upload_batch_utc", DateTime, comment="Date/time when the device's last upload batch started (UTC)" ) ongoing_upload_batch_utc = Column( "ongoing_upload_batch_utc", DateTime, comment="Date/time when the device's ongoing upload batch " "started (UTC)" ) uploading_user_id = Column( "uploading_user_id", Integer, ForeignKey("_security_users.id"), comment="ID of user in the process of uploading right now" ) uploading_user = relationship("User", foreign_keys=[uploading_user_id]) currently_preserving = Column( "currently_preserving", Boolean, default=False, comment="Preservation currently in progress" ) @classmethod def get_device_by_name(cls, dbsession: SqlASession, device_name: str) -> Optional['Device']: if not device_name: return None device = dbsession.query(cls)\ .filter(cls.name == device_name)\ .first() # type: Optional[Device] return device @classmethod def get_device_by_id(cls, dbsession: SqlASession, device_id: int) -> Optional['Device']: if device_id is None: return None device = dbsession.query(cls)\ .filter(cls.id == device_id)\ .first() # type: Optional[Device] return device @classmethod def get_server_device(cls, dbsession: SqlASession) -> "Device": device = cls.get_device_by_name(dbsession, DEVICE_NAME_FOR_SERVER) if device is None: device = Device() device.name = DEVICE_NAME_FOR_SERVER device.friendly_name = "CamCOPS server" device.registered_by_user = User.get_system_user(dbsession) device.when_registered_utc = Pendulum.utcnow() device.camcops_version = CAMCOPS_SERVER_VERSION dbsession.add(device) return device
[docs] def get_friendly_name(self) -> str: """Get device friendly name (or failing that, device name).""" if self.friendly_name is None: return self.name return self.friendly_name
[docs] def get_friendly_name_and_id(self) -> str: """Get device ID with friendly name (or failing that, device name).""" if self.friendly_name is None: return self.name return "{} (device# {}, {})".format(self.name, self.id, self.friendly_name)
[docs] def get_id(self) -> int: """Get device ID.""" return self.id
[docs] def is_valid(self) -> bool: """ Valid device (having been instantiated with Device(device_id), i.e. is it in the database? """ return self.id is not None
# ============================================================================= # Reports # =============================================================================
[docs]class DeviceReport(Report): """ Report to show registered devices. This is a superuser-only report, so we do not override superuser_only. """ # noinspection PyMethodParameters @classproperty def report_id(cls) -> str: return "devices" # noinspection PyMethodParameters @classproperty def title(cls) -> str: return "(Server) Devices registered with the server"
[docs] def get_query(self, req: "CamcopsRequest") -> Query: dbsession = req.dbsession query = ( dbsession.query(Device.id, Device.name, Device.registered_by_user_id, Device.when_registered_utc, Device.friendly_name, Device.camcops_version, Device.last_upload_batch_utc) .order_by(Device.id) ) return query
# ============================================================================= # Unit tests # =============================================================================
[docs]class DeviceTests(DemoDatabaseTestCase): def test_device(self) -> None: self.announce("test_device") q = self.dbsession.query(Device) d = q.first() # type: Device assert d, "Missing Device in demo database!" self.assertIsInstance(d.get_friendly_name(), str) self.assertIsInstance(d.get_id(), int) self.assertIsInstance(d.is_valid(), bool)