Exemplo n.º 1
0
def test_item_timestamps(app_client, load_test_data):
    """Test created and updated timestamps (common metadata)"""
    test_item = load_test_data("test_item.json")
    start_time = datetime.now(timezone.utc)
    time.sleep(2)
    # Confirm `created` timestamp
    resp = app_client.post(f"/collections/{test_item['collection']}/items",
                           json=test_item)
    item = resp.json()
    created_dt = parse_datetime(item["properties"]["created"])
    assert resp.status_code == 200
    assert start_time < created_dt < datetime.now(timezone.utc)

    time.sleep(2)
    # Confirm `updated` timestamp
    item["properties"]["proj:epsg"] = 4326
    resp = app_client.put(f"/collections/{test_item['collection']}/items",
                          json=item)
    assert resp.status_code == 200
    updated_item = resp.json()

    # Created shouldn't change on update
    assert item["properties"]["created"] == updated_item["properties"][
        "created"]
    assert parse_datetime(updated_item["properties"]["updated"]) > created_dt
Exemplo n.º 2
0
 def end_date(self) -> Optional[datetime]:
     values = self.datetime.split("/")
     if len(values) == 1:
         return parse_datetime(values[0])
     if values[1] == "..":
         return None
     return parse_datetime(values[1])
Exemplo n.º 3
0
    def end_date(self) -> Optional[datetime]:
        """Extract the end date from the datetime string."""
        if not self.datetime:
            return

        values = self.datetime.split("/")
        if len(values) == 1:
            return parse_datetime(values[0])
        if values[1] == "..":
            return None
        return parse_datetime(values[1])
Exemplo n.º 4
0
    async def _run(self, ws):
        api = StreamingApi(ws, self._state)
        try:
            funcs = self._get_handlers(ServiceEventName.startup)
            await asyncio.gather(*[Func(func, api)() for func in funcs])

            async for msg in ws:
                if msg.type == aiohttp.WSMsgType.TEXT:
                    data = msg.json()

                    event_name = data['event']
                    payload = data['payload']
                    server_time = parse_datetime(data['time'])

                    if event_name in self.schemas:
                        data = self.schemas[event_name].parse_obj(payload)
                    else:
                        data = payload

                    await asyncio.gather(*self._call_handlers(
                        event_name, api, data, server_time))

                elif msg.type == aiohttp.WSMsgType.CLOSED:
                    break
                elif msg.type == aiohttp.WSMsgType.ERROR:
                    break
            await self._cleanup(api)
        except asyncio.CancelledError:
            await self._cleanup(api)
            raise
Exemplo n.º 5
0
def impl_AllergyIntolerance_1(inst):
    assert inst.category == "food"
    assert (
        inst.extension[0].url ==
        "http://hl7.org/fhir/StructureDefinition/allergyintolerance-reasonRefuted"
    )
    assert inst.extension[0].valueCodeableConcept.coding[0].code == "MED"
    assert (inst.extension[0].valueCodeableConcept.coding[0].display ==
            "Medical Status Altered")
    assert (inst.extension[0].valueCodeableConcept.coding[0].system ==
            "http://hl7.org/fhir/v3/ActReason")
    assert inst.id == "allergyintolerance-example-refuted"
    assert inst.identifier[0].system == "http://acme.com/ids/patients/risks"
    assert inst.identifier[0].value == "49476534"
    assert inst.patient.reference == "Patient/example"
    assert inst.recordedDate == parse_datetime("2014-10-09T14:58:00+11:00")
    assert inst.recorder.reference == "Practitioner/example"
    assert inst.status == "refuted"
    assert inst.substance.coding[0].code == "227493005"
    assert inst.substance.coding[0].display == "Cashew nuts"
    assert inst.substance.coding[0].system == "http://snomed.info/sct"
    assert (
        inst.text.div ==
        "<div><p><b>Generated Narrative with Details</b></p><p><b>id</b>: allergyintolerance-example-refuted</p><p><b>identifier</b>: 49476534</p><p><b>recordedDate</b>: 09/10/2014 2:58:00 PM</p><p><b>recorder</b>: <a>Practitioner/example</a></p><p><b>patient</b>: <a>Patient/example</a></p><p><b>substance</b>: Cashew nuts <span>(Details : {SNOMED CT code '227493005' = '227493005', given as 'Cashew nuts'})</span></p><p><b>status</b>: refuted</p><p><b>category</b>: food</p></div>"
    )
    assert inst.text.status == "generated"
Exemplo n.º 6
0
async def store_click(ctx, *, link_id, ip, ts, user_agent):
    cache_key = f'click-{link_id}-{ip}'
    with await ctx['redis'] as redis:
        v = await redis.incr(cache_key)
        if v > 1:
            return 'recently_clicked'
        await redis.expire(cache_key, 60)

    async with ctx['pg'].acquire() as conn:
        message_id, target = await conn.fetchrow(
            'select message_id, url from links where id=$1', link_id)
        extra = {'target': target, 'ip': ip, 'user_agent': user_agent}
        if user_agent:
            ua_dict = ParseUserAgent(user_agent)
            platform = ua_dict['device']['family']
            if platform in {'Other', None}:
                platform = ua_dict['os']['family']
            extra['user_agent_display'] = ((
                '{user_agent[family]} {user_agent[major]} on '
                '{platform}').format(platform=platform, **ua_dict).strip(' '))

        ts = parse_datetime(ts)
        status = 'click'
        await conn.execute_b(
            'insert into events (:values__names) values :values',
            values=Values(message_id=message_id,
                          status=status,
                          ts=ts,
                          extra=json.dumps(extra)),
        )
Exemplo n.º 7
0
    def parse_last_modified(cls, value: Any) -> Optional[datetime]:
        """Parse last_modified value."""
        if value is None:
            return None

        if not isinstance(value, str):
            raise ValueError("must be a string")

        if value.strip() == "-":
            return None

        try:
            parsed_datetime = parse_datetime(value)
            if parsed_datetime.tzinfo is None:
                parsed_datetime = parsed_datetime.replace(tzinfo=timezone.utc)
            return parsed_datetime
        except DateTimeError:
            # Ignore
            pass

        try:
            date_value = parse_date(value)
            return datetime.combine(date_value,
                                    datetime.min.time(),
                                    tzinfo=timezone.utc)
        except DateError:
            log.error("Unable to parse last_modified value: %s", value)
            return None
Exemplo n.º 8
0
def impl_Device_5(inst):
    assert inst.contact[0].system == "phone"
    assert inst.contact[0].value == "ext 4352"
    assert inst.id == "example"
    assert inst.identifier[0].system == "http://goodcare.org/devices/id"
    assert inst.identifier[0].value == "345675"
    assert inst.identifier[1].type.coding[0].code == "SNO"
    assert (inst.identifier[1].type.coding[0].system ==
            "http://hl7.org/fhir/identifier-type")
    assert inst.identifier[1].type.text == "Serial Number"
    assert inst.identifier[1].value == "AMID-342135-8464"
    assert inst.lotNumber == "43453424"
    assert inst.manufacturer == "Acme Devices, Inc"
    assert inst.model == "AB 45-J"
    assert inst.note[0].authorReference.reference == "Practitioner/xcda-author"
    assert inst.note[0].text == "QA Checked"
    assert inst.note[0].time == parse_datetime("2015-06-28T14:03:32+10:00")
    assert inst.status == "available"
    assert (
        inst.text.div ==
        "<div><p><b>Generated Narrative with Details</b></p><p><b>id</b>: example</p><p><b>identifier</b>: 345675, Serial Number = AMID-342135-8464</p><p><b>type</b>: ECG <span>(Details : {SNOMED CT code '86184003' = '86184003', given as 'Electrocardiographic monitor and recorder'})</span></p><p><b>note</b>: QA Checked</p><p><b>status</b>: available</p><p><b>manufacturer</b>: Acme Devices, Inc</p><p><b>model</b>: AB 45-J</p><p><b>lotNumber</b>: 43453424</p><p><b>contact</b>: ph: ext 4352</p></div>"
    )
    assert inst.text.status == "generated"
    assert inst.type.coding[0].code == "86184003"
    assert inst.type.coding[
        0].display == "Electrocardiographic monitor and recorder"
    assert inst.type.coding[0].system == "http://snomed.info/sct"
    assert inst.type.text == "ECG"
Exemplo n.º 9
0
def impl_DocumentManifest_1(inst):
    assert inst.author[0].reference == "#a1"
    assert inst.contained[0].id == "a1"
    assert inst.contained[0].name.family[0] == "Dopplemeyer"
    assert inst.contained[0].name.given[0] == "Sherry"
    assert (inst.contained[0].practitionerRole[0].managingOrganization.display
            == "Cleveland Clinic")
    assert inst.contained[0].practitionerRole[0].role.text == "Primary Surgon"
    assert inst.contained[0].practitionerRole[0].specialty[
        0].text == "Orthopedic"
    assert inst.contained[0].telecom[0].system == "email"
    assert inst.contained[0].telecom[
        0].value == "*****@*****.**"
    assert inst.content[0].pReference.reference == "DocumentReference/example"
    assert inst.created == parse_datetime("2004-12-25T23:50:50-05:00")
    assert inst.description == "Physical"
    assert inst.id == "example"
    assert inst.identifier[0].system == "http://example.org/documents"
    assert inst.identifier[0].value == "23425234234-2347"
    assert inst.masterIdentifier.system == "http://example.org/documents"
    assert inst.masterIdentifier.value == "23425234234-2346"
    assert inst.recipient[0].reference == "Practitioner/xcda1"
    assert inst.related[0].identifier.system == "http://example.org/documents"
    assert inst.related[0].identifier.value == "23425234234-9999"
    assert inst.related[0].ref.reference == "DocumentReference/example"
    assert inst.source == "urn:oid:1.3.6.1.4.1.21367.2009.1.2.1"
    assert inst.status == "current"
    assert inst.subject.reference == "Patient/xcda"
    assert inst.text.div == "<div>Text</div>"
    assert inst.text.status == "generated"
    assert inst.type.text == "History and Physical"
Exemplo n.º 10
0
def impl_Communication_1(inst):
    assert inst.category.coding[0].code == "Alert"
    assert inst.category.coding[0].system == "http://acme.org/messagetypes"
    assert inst.category.text == "Alert"
    assert (
        inst.extension[0].url ==
        "http://hl7.org/fhir/StructureDefinition/communication-reasonNotPerformed"
    )
    assert inst.extension[0].valueCodeableConcept.coding[0].code == "EIE"
    assert (inst.extension[0].valueCodeableConcept.coding[0].display ==
            "entered in error")
    assert (inst.extension[0].valueCodeableConcept.coding[0].system ==
            "http://hl7.org/fhir/v3/ActReason")
    assert inst.id == "communication-example"
    assert inst.identifier[0].system == "urn:oid:1.3.4.5.6.7"
    assert inst.identifier[0].type.text == "Paging System"
    assert inst.identifier[0].value == "2345678901"
    assert (
        inst.payload[0].contentString ==
        "Patient 1 has a very high serum potassium value (7.2 mmol/L on 2014-Dec-12 at 5:55 pm)"
    )
    assert inst.payload[
        1].contentReference.reference == "Observation/643666aa12f"
    assert inst.recipient[0].reference == "Practitioner/21"
    assert inst.sender.reference == "Device/f001"
    assert inst.sent == parse_datetime("2014-12-12T18:01:10-08:00")
    assert inst.status == "suspended"
    assert inst.subject.reference == "Patient/1"
    assert inst.text.div == "<div>Patient has very high serum potassium</div>"
    assert inst.text.status == "generated"
Exemplo n.º 11
0
    def validate_datetime(cls, v, values):
        if v == "null":
            if not values["start_datetime"] and not values["end_datetime"]:
                raise ValueError(
                    "start_datetime and end_datetime must be specified when datetime is null"
                )

        if isinstance(v, str):
            return parse_datetime(v)

        return v
Exemplo n.º 12
0
 def __eq__(self, other):
     self.other = other
     if not isinstance(other, datetime):
         try:
             from pydantic.datetime_parse import parse_datetime
         except ImportError:  # pragma: no cover
             raise ImportError('pydantic is required to use CloseToNow, please run `pip install pydantic`')
         other = parse_datetime(other)
     if other.tzinfo:
         self.now = self.now.replace(tzinfo=timezone.utc)
     self.match = -self.delta < (self.now - other).total_seconds() < self.delta
     return self.match
Exemplo n.º 13
0
def convert_to_datetime(val: str) -> datetime:
    constants = {
        'now': datetime.utcnow(),
        'day': datetime.utcnow() - timedelta(days=1),
        'week': datetime.utcnow() - timedelta(days=7),
        'month': datetime.utcnow() - timedelta(days=31),
        '6month': datetime.utcnow() - timedelta(days=31 * 6),
        'year': datetime.utcnow() - timedelta(days=365),
    }
    if val in constants:
        return constants[val]
    return parse_datetime(val)
Exemplo n.º 14
0
    async def build(cls, request_body: Union[str, bytes],
                    http_client: AsyncClient) -> Optional['SesWebhookInfo']:
        payload = await sns.verify_webhook(request_body, http_client)
        if not payload:
            # happens legitimately for subscription confirmation webhooks
            return None

        try:
            message = json.loads(payload.message)
        except ValueError:
            # this can happen legitimately, e.g. when a new configuration set is setup
            logger.warning('invalid JSON in SNS notification',
                           extra={'data': {
                               'request': payload.request_data
                           }})
            return None

        event_type = message['eventType'].lower()

        if event_type not in {
                'send', 'delivery', 'open', 'click', 'bounce', 'complaint'
        }:
            logger.warning('unknown aws webhook event %s',
                           event_type,
                           extra={'data': {
                               'request': payload.request_data
                           }})

        message_id = message['mail']['messageId']
        logger.info('%s for message %s', event_type, message_id)

        details = message.get(event_type) or {}
        mail = message.get('mail') or {}
        tags = mail.get('tags') or {}
        timestamp = details.get('timestamp') or mail.get('timestamp')

        if event_type == 'bounce':
            unsubscribe = details.get('bounceType') == 'Permanent'
        elif event_type == 'complaint':
            unsubscribe = True
        else:
            unsubscribe = False
        return cls(
            message_id=message_id,
            event_type=event_type,
            timestamp=timestamp and parse_datetime(timestamp),
            unsubscribe=unsubscribe,
            tags={k: v[0]
                  for k, v in tags.items()},
            details=details,
            full_message=message,
            request_data=payload.request_data,
        )
Exemplo n.º 15
0
 def validate(cls, v: Any) -> datetime:
     if isinstance(v, datetime):
         d = v
     else:
         d = parse_datetime(v)
     # MongoDB does not store timezone info
     # https://docs.python.org/3/library/datetime.html#determining-if-an-object-is-aware-or-naive
     if d.tzinfo is not None and d.tzinfo.utcoffset(d) is not None:
         raise ValueError("datetime objects must be naive (no timeone info)")
     # Round microseconds to the nearest millisecond to comply with Mongo behavior
     microsecs = round(d.microsecond / 1000) * 1000
     return d.replace(microsecond=microsecs)
Exemplo n.º 16
0
    def validate_datetime(cls, v):
        if "/" in v:
            values = v.split("/")
        else:
            # Single date is interpreted as end date
            values = ["..", v]

        dates = []
        for value in values:
            if value == "..":
                dates.append(value)
                continue

            parse_datetime(value)
            dates.append(value)

        if ".." not in dates:
            if parse_datetime(dates[0]) > parse_datetime(dates[1]):
                raise ValueError(
                    "Invalid datetime range, must match format (begin_date, end_date)"
                )

        return v
Exemplo n.º 17
0
def impl_AllergyIntolerance_3(inst):
    assert inst.category == "food"
    assert inst.id == "fishallergy"
    assert inst.identifier[0].system == "http://acme.com/ids/patients/risks"
    assert inst.identifier[0].value == "49476535"
    assert inst.patient.reference == "Patient/example"
    assert inst.recordedDate == parse_datetime("2015-08-06T15:37:31-06:00")
    assert inst.recorder.reference == "Practitioner/example"
    assert inst.substance.coding[0].code == "227037002"
    assert inst.substance.coding[0].display == "Fish - dietary (substance)"
    assert inst.substance.coding[0].system == "http://snomed.info/sct"
    assert inst.substance.text == "Allergic to fresh fish. Tolerates canned fish"
    assert (inst.text.div == """<div>
      <p>allergy is to fresh fish. Tolerates canned fish</p>
      <p>recordedDate:2015-08-06T00:00:00-06:00</p>
      <p>substance:Fish - dietary (substance)</p>
    </div>""")
    assert inst.text.status == "additional"
Exemplo n.º 18
0
def impl_AllergyIntolerance_2(inst):
    assert inst.category == "food"
    assert inst.criticality == "CRITH"
    assert inst.id == "allergyintolerance-example"
    assert inst.identifier[0].system == "http://acme.com/ids/patients/risks"
    assert inst.identifier[0].value == "49476534"
    assert inst.lastOccurence == "2012-06"
    assert inst.patient.reference == "Patient/example"
    assert inst.reaction[0].certainty == "confirmed"
    assert (
        inst.reaction[0].description ==
        "Challenge Protocol. Severe Reaction to 1/8 cashew. Epinephrine administered"
    )
    assert inst.reaction[0].manifestation[0].coding[0].code == "39579001"
    assert (inst.reaction[0].manifestation[0].coding[0].display ==
            "Anaphylactic reaction")
    assert (inst.reaction[0].manifestation[0].coding[0].system ==
            "http://snomed.info/sct")
    assert inst.reaction[0].onset == parse_date("2012-06-12")
    assert inst.reaction[0].severity == "severe"
    assert inst.reaction[0].substance.coding[0].code == "C3214954"
    assert (inst.reaction[0].substance.coding[0].display ==
            "cashew nut allergenic extract Injectable Product")
    assert (inst.reaction[0].substance.coding[0].system ==
            "http://www.nlm.nih.gov/research/umls/rxnorm")
    assert inst.reaction[1].certainty == "likely"
    assert inst.reaction[1].manifestation[0].coding[0].code == "64305001"
    assert inst.reaction[1].manifestation[0].coding[0].display == "Urticaria"
    assert (inst.reaction[1].manifestation[0].coding[0].system ==
            "http://snomed.info/sct")
    assert inst.reaction[1].onset == "2004"
    assert inst.reaction[1].severity == "moderate"
    assert inst.recordedDate == parse_datetime("2014-10-09T14:58:00+11:00")
    assert inst.recorder.reference == "Practitioner/example"
    assert inst.status == "confirmed"
    assert inst.substance.coding[0].code == "227493005"
    assert inst.substance.coding[0].display == "Cashew nuts"
    assert inst.substance.coding[0].system == "http://snomed.info/sct"
    assert (
        inst.text.div ==
        "<div><p><b>Generated Narrative with Details</b></p><p><b>id</b>: allergyintolerance-example</p><p><b>identifier</b>: 49476534</p><p><b>recordedDate</b>: 09/10/2014 2:58:00 PM</p><p><b>recorder</b>: <a>Practitioner/example</a></p><p><b>patient</b>: <a>Patient/example</a></p><p><b>substance</b>: Cashew nuts <span>(Details : {SNOMED CT code '227493005' = '227493005', given as 'Cashew nuts'})</span></p><p><b>status</b>: confirmed</p><p><b>criticality</b>: CRITH</p><p><b>type</b>: allergy</p><p><b>category</b>: food</p><p><b>lastOccurence</b>: 01/06/2012</p><blockquote><p><b>reaction</b></p><p><b>substance</b>: cashew nut allergenic extract Injectable Product <span>(Details : {RxNorm code 'C3214954' = '??', given as 'cashew nut allergenic extract Injectable Product'})</span></p><p><b>certainty</b>: confirmed</p><p><b>manifestation</b>: Anaphylactic reaction <span>(Details : {SNOMED CT code '39579001' = '39579001', given as 'Anaphylactic reaction'})</span></p><p><b>description</b>: Challenge Protocol. Severe Reaction to 1/8 cashew. Epinephrine administered</p><p><b>onset</b>: 12/06/2012</p><p><b>severity</b>: severe</p></blockquote><blockquote><p><b>reaction</b></p><p><b>certainty</b>: likely</p><p><b>manifestation</b>: Urticaria <span>(Details : {SNOMED CT code '64305001' = '64305001', given as 'Urticaria'})</span></p><p><b>onset</b>: 01/01/2004</p><p><b>severity</b>: moderate</p></blockquote></div>"
    )
    assert inst.text.status == "generated"
    assert inst.type == "allergy"
Exemplo n.º 19
0
    def validate(cls, value) -> datetime:
        """ Validate/parse the datetime """
        if isinstance(value, datetime):
            # It's already a datetime!
            return value

        if not isinstance(value, str):
            raise ValueError(f"string: str expected not {type(value)}")

        match = Datetime.regex.match(value)  # type: ignore
        if match:

            kwargs: Dict[str, Union[int, timezone]] = {
                k: int(v)
                for k, v in match.groupdict().items()
            }
            return datetime(tzinfo=timezone.utc, **kwargs)  # type: ignore

        return datetime_parse.parse_datetime(value)
Exemplo n.º 20
0
def test_datetime_parsing(value, result):
    if result == errors.DateTimeError:
        with pytest.raises(errors.DateTimeError):
            parse_datetime(value)
    else:
        assert parse_datetime(value) == result
Exemplo n.º 21
0
def init_osm_update(ctx):
    """
    Init osmosis folder with configuration files and
    latest state.txt file before .pbf timestamp
    """
    logging.info("initializing osm update...")
    session = requests.Session()

    class OsmState(BaseModel):
        """
        ConfigParser uses lowercased keys
        "sequenceNumber" from state.txt is renamed to "sequencenumber"
        """
        sequencenumber: int
        timestamp: datetime

    def get_state_url(sequence_number=None):
        base_url = ctx.osm_update.replication_url
        if sequence_number is None:
            # Get last state.txt
            return f'{base_url}/state.txt'
        else:
            return f'{base_url}' \
                f'/{sequence_number // 1_000_000 :03d}' \
                f'/{sequence_number // 1000 % 1000 :03d}' \
                f'/{sequence_number % 1000 :03d}.state.txt'

    def get_state(sequence_number=None):
        url = get_state_url(sequence_number)
        resp = session.get(url)
        resp.raise_for_status()
        # state file may contain escaped ':' in the timestamp
        state_string = resp.text.replace('\:', ':')
        c = configparser.ConfigParser()
        c.read_string('[root]\n' + state_string)
        return OsmState(**c['root'])

    # Init osmosis working directory
    ctx.run(f'mkdir -p {ctx.update_tiles_dir}')
    ctx.run(f'touch {ctx.update_tiles_dir}/download.lock')

    raw_osm_datetime = ctx.run(
        f'osmconvert {ctx.osm.file} --out-timestamp').stdout
    osm_datetime = parse_datetime(raw_osm_datetime)
    # Rewind 2 hours as a precaution
    osm_datetime -= timedelta(hours=2)

    last_state = get_state()
    sequence_number = last_state.sequencenumber
    sequence_dt = last_state.timestamp

    for i in range(ctx.osm_update.max_interations):
        if sequence_dt < osm_datetime:
            break
        sequence_number -= 1
        state = get_state(sequence_number)
        sequence_dt = state.timestamp
    else:
        logging.error(
            "Failed to init osm update. "
            "Could not find a replication sequence before %s",
            osm_datetime,
        )
        return

    state_url = get_state_url(sequence_number)
    ctx.run(f'wget -q "{state_url}" -O {ctx.update_tiles_dir}/state.txt')

    with open(f'{ctx.update_tiles_dir}/configuration.txt', 'w') as conf_file:
        conf_file.write(f'baseUrl={ctx.osm_update.replication_url}\n')
        conf_file.write(f'maxInterval={ctx.osm_update.max_interval}\n')
Exemplo n.º 22
0
    async def record_email_event(self, raw_message: str):
        """
        record email events
        """
        message = json.loads(raw_message)
        msg_id = message['mail']['messageId']
        r = await self.pg.fetchrow(
            'select id, user_id, update_ts from emails where ext_id=$1',
            msg_id)
        if not r:
            return
        email_id, user_id, last_updated = r

        event_type = message.get('eventType')
        extra = None
        data = message.get(event_type.lower()) or {}
        if event_type == 'Send':
            data = message['mail']
        elif event_type == 'Delivery':
            extra = {
                'delivery_time': data.get('processingTimeMillis'),
            }
        elif event_type == 'Open':
            extra = {
                'ip': data.get('ipAddress'),
                'ua': data.get('userAgent'),
            }
        elif event_type == 'Click':
            extra = {
                'link': data.get('link'),
                'ip': data.get('ipAddress'),
                'ua': data.get('userAgent'),
            }
        elif event_type == 'Bounce':
            extra = {
                'bounceType': data.get('bounceType'),
                'bounceSubType': data.get('bounceSubType'),
                'reportingMTA': data.get('reportingMTA'),
                'feedbackId': data.get('feedbackId'),
                'unsubscribe': data.get('bounceType') == 'Permanent',
            }
        elif event_type == 'Complaint':
            extra = {
                'complaintFeedbackType': data.get('complaintFeedbackType'),
                'feedbackId': data.get('feedbackId'),
                'ua': data.get('userAgent'),
                'unsubscribe': True
            }
        else:
            logger.warning('unknown aws webhooks %s',
                           event_type,
                           extra={'data': {
                               'message': message
                           }})

        values = dict(email=email_id, status=event_type)
        ts = None
        if data.get('timestamp'):
            ts = parse_datetime(data['timestamp'])
            values['ts'] = ts
        if extra:
            values['extra'] = json.dumps({k: v for k, v in extra.items() if v})

        async with self.pg.acquire() as conn:
            await conn.execute_b(
                'insert into email_events (:values__names) values :values',
                values=Values(**values))
            if not ts:
                await conn.execute(
                    'update emails set status=$1, update_ts=CURRENT_TIMESTAMP where id=$2',
                    event_type, email_id)
            elif last_updated < ts:
                await conn.execute(
                    'update emails set status=$1, update_ts=$2 where id=$3',
                    event_type, ts, email_id)

            if extra and extra.get('unsubscribe'):
                await conn.execute(
                    'update users set receive_emails=false where id=$1',
                    user_id)

        return event_type
def is_datetime(value: Union[datetime, StrBytesIntFloat]) -> bool:
    try:
        parse_datetime(value)
        return isinstance(value, (str, datetime))
    except:
        return False
Exemplo n.º 24
0
def convert_to_datetime(s):
    return parse_datetime(s).astimezone()
Exemplo n.º 25
0
def replace_data(m):
    dt = parse_datetime(m.group())
    # WARNING: this means the output is not valid json, but is more readable
    return f'{m.group()} ({dt:%a %Y-%m-%d %H:%M})'
Exemplo n.º 26
0
def test_datetime_parsing(value, result):
    if type(result) == type and issubclass(result, Exception):
        with pytest.raises(result):
            parse_datetime(value)
    else:
        assert parse_datetime(value) == result
Exemplo n.º 27
0
def impl_DocumentReference_1(inst):
    assert inst.authenticator.reference == "Organization/organization-example"
    assert inst.author[0].reference == "Practitioner/xcda1"
    assert inst.author[1].reference == "#a2"
    assert inst.contained[0].id == "a2"
    assert inst.contained[0].name.family[0] == "Smitty"
    assert inst.contained[0].name.given[0] == "Gerald"
    assert (
        inst.contained[0].practitionerRole[0].managingOrganization.display
        == "Cleveland Clinic"
    )
    assert inst.contained[0].practitionerRole[0].role.text == "Attending"
    assert inst.contained[0].practitionerRole[0].specialty[0].text == "Orthopedic"
    assert inst.content[0].attachment.contentType == "application/hl7-v3+xml"
    assert inst.content[0].attachment.hash == b"2jmj7l5rSw0yVb/vlWAYkK/YBwk="
    assert inst.content[0].attachment.language == "en-US"
    assert inst.content[0].attachment.size == 3654
    assert (
        inst.content[0].attachment.url
        == "http://example.org/xds/mhd/Binary/07a6483f-732b-461e-86b6-edb665c45510"
    )
    assert inst.content[0].format[0].code == "urn:ihe:pcc:handp:2008"
    assert inst.content[0].format[0].display == "History and Physical Specification"
    assert inst.content[0].format[0].system == "urn:oid:1.3.6.1.4.1.19376.1.2.3"
    assert inst.context.encounter.reference == "Encounter/xcda"
    assert inst.context.event[0].coding[0].code == "T-D8200"
    assert inst.context.event[0].coding[0].display == "Arm"
    assert (
        inst.context.event[0].coding[0].system
        == "http://ihe.net/xds/connectathon/eventCodes"
    )
    assert inst.context.facilityType.coding[0].code == "Outpatient"
    assert inst.context.facilityType.coding[0].display == "Outpatient"
    assert (
        inst.context.facilityType.coding[0].system
        == "http://www.ihe.net/xds/connectathon/healthcareFacilityTypeCodes"
    )
    assert inst.context.period.end == parse_datetime("2004-12-23T08:01:00+11:00")
    assert inst.context.period.start == parse_datetime("2004-12-23T08:00:00+11:00")
    assert inst.context.practiceSetting.coding[0].code == "General Medicine"
    assert inst.context.practiceSetting.coding[0].display == "General Medicine"
    assert (
        inst.context.practiceSetting.coding[0].system
        == "http://www.ihe.net/xds/connectathon/practiceSettingCodes"
    )
    assert inst.context.related[0].identifier.system == "urn:ietf:rfc:3986"
    assert (
        inst.context.related[0].identifier.value
        == "urn:oid:1.3.6.1.4.1.21367.2005.3.7.2345"
    )
    assert inst.context.related[0].ref.reference == "Patient/xcda"
    assert inst.context.sourcePatientInfo.reference == "Patient/xcda"
    assert inst.created == parse_datetime("2005-12-24T09:35:00+11:00")
    assert inst.custodian.reference == "Organization/organization-example"
    assert inst.description == "Physical"
    assert inst.docStatus.coding[0].code == "preliminary"
    assert inst.docStatus.coding[0].display == "preliminary"
    assert inst.docStatus.coding[0].system == "http://hl7.org/fhir/composition-status"
    assert inst.id == "example"
    assert inst.identifier[0].system == "urn:ietf:rfc:3986"
    assert inst.identifier[0].value == "urn:oid:1.3.6.1.4.1.21367.2005.3.7.1234"
    assert inst.indexed == parse_datetime("2005-12-24T09:43:41+11:00")
    assert inst.masterIdentifier.system == "urn:ietf:rfc:3986"
    assert inst.masterIdentifier.value == "urn:oid:1.3.6.1.4.1.21367.2005.3.7"
    assert inst.relatesTo[0].code == "appends"
    assert inst.relatesTo[0].target.reference == "DocumentReference/example"
    assert inst.securityLabel[0].coding[0].code == "V"
    assert inst.securityLabel[0].coding[0].display == "very restricted"
    assert (
        inst.securityLabel[0].coding[0].system
        == "http://hl7.org/fhir/v3/Confidentiality"
    )
    assert inst.status == "current"
    assert inst.subject.reference == "Patient/xcda"
    assert (
        inst.text.div
        == "<div><p><b>Generated Narrative with Details</b></p><p><b>id</b>: example</p><p><b>contained</b>: </p><p><b>masterIdentifier</b>: urn:oid:1.3.6.1.4.1.21367.2005.3.7</p><p><b>identifier</b>: urn:oid:1.3.6.1.4.1.21367.2005.3.7.1234</p><p><b>subject</b>: <a>Patient/xcda</a></p><p><b>type</b>: Outpatient Note <span>(Details : {LOINC code '34108-1' = 'Outpatient Note', given as 'Outpatient Note'})</span></p><p><b>class</b>: History and Physical <span>(Details : {http://ihe.net/xds/connectathon/classCodes code 'History and Physical' = '??', given as 'History and Physical'})</span></p><p><b>author</b>: <a>Practitioner/xcda1</a>, id: a2; Gerald Smitty </p><p><b>custodian</b>: <a>Organization/organization-example</a></p><p><b>authenticator</b>: <a>Organization/organization-example</a></p><p><b>created</b>: 24/12/2005 9:35:00 AM</p><p><b>indexed</b>: 24/12/2005 9:43:41 AM</p><p><b>status</b>: current</p><p><b>docStatus</b>: preliminary <span>(Details : {http://hl7.org/fhir/composition-status code 'preliminary' = 'Preliminary', given as 'preliminary'})</span></p><h3>RelatesTos</h3><table><tr><td>-</td><td><b>Code</b></td><td><b>Target</b></td></tr><tr><td>*</td><td>appends</td><td><a>DocumentReference/example</a></td></tr></table><p><b>description</b>: Physical</p><p><b>securityLabel</b>: very restricted <span>(Details : {http://hl7.org/fhir/v3/Confidentiality code 'V' = 'very restricted', given as 'very restricted'})</span></p><h3>Contents</h3><table><tr><td>-</td><td><b>Attachment</b></td><td><b>Format</b></td></tr><tr><td>*</td><td/><td>History and Physical Specification (Details: urn:oid:1.3.6.1.4.1.19376.1.2.3 code urn:ihe:pcc:handp:2008 = '??', stated as 'History and Physical Specification')</td></tr></table><blockquote><p><b>context</b></p><p><b>encounter</b>: <a>Encounter/xcda</a></p><p><b>event</b>: Arm <span>(Details : {http://ihe.net/xds/connectathon/eventCodes code 'T-D8200' = '??', given as 'Arm'})</span></p><p><b>period</b>: 23/12/2004 8:00:00 AM --&gt; 23/12/2004 8:01:00 AM</p><p><b>facilityType</b>: Outpatient <span>(Details : {http://www.ihe.net/xds/connectathon/healthcareFacilityTypeCodes code 'Outpatient' = '??', given as 'Outpatient'})</span></p><p><b>practiceSetting</b>: General Medicine <span>(Details : {http://www.ihe.net/xds/connectathon/practiceSettingCodes code 'General Medicine' = '??', given as 'General Medicine'})</span></p><p><b>sourcePatientInfo</b>: <a>Patient/xcda</a></p><h3>Relateds</h3><table><tr><td>-</td><td><b>Identifier</b></td><td><b>Ref</b></td></tr><tr><td>*</td><td>urn:oid:1.3.6.1.4.1.21367.2005.3.7.2345</td><td><a>Patient/xcda</a></td></tr></table></blockquote></div>"
    )
    assert inst.text.status == "generated"
    assert inst.type.coding[0].code == "34108-1"
    assert inst.type.coding[0].display == "Outpatient Note"
    assert inst.type.coding[0].system == "http://loinc.org"
Exemplo n.º 28
0
async def update_user(
    user_data: DotDict,
    update_data: Dict[str, Any],
    is_new: bool = False,
    is_registering: bool = False,
    is_admin: bool = False,
    is_self: bool = False,
    no_registration: bool = False,
):
    if 'sub' in update_data or '_id' in update_data or 'picture' in update_data:
        raise HTTPException(400, f"Cannot modify 'sub', '_id' or 'picture'")
    was_active = user_data.get('active', False)
    reset_user_cache = False

    if is_new:
        assert '_id' not in user_data
        user_data['_id'] = generate_token(48)

    if 'password' in update_data:
        if not isinstance(update_data['password'], str):
            raise HTTPException(400, "'password' must be a string")
        _validate_property_write('password', is_self, is_admin)
        if is_self and not is_registering and user_data.get(
                'password') is not None:
            if 'old_password' not in update_data:
                raise HTTPException(
                    400, f"Need {repr('old_password')} for setting password")
            if not isinstance(update_data['old_password'], str):
                raise HTTPException(400,
                                    f"{repr('old_password')} is not a string")
            is_valid, _ = verify_and_update(update_data['old_password'],
                                            user_data['password'])
            if not is_valid:
                raise HTTPException(401, "Old password does not match")
        try:
            user_data['password'] = create_password(update_data['password'])
            del update_data['password']
        except PasswordLeakedException:
            raise HTTPException(
                400,
                "Password is leaked and cannot be used. See https://haveibeenpwned.com/"
            )

    async def send_mail():
        pass

    if is_registering and update_data.get(
            'email', user_data['email']) == user_data['email']:
        user_data['email_verified'] = True
    elif 'email' in update_data:
        if not isinstance(update_data['email'], str):
            raise HTTPException(400, "'email' must be a string")
        _validate_property_write('email', is_self, is_admin)
        if not is_email(update_data['email'], check_dns=True):
            raise HTTPException(400, "E-Mail address not accepted")
        if await async_user_collection.count_documents(
            {'email': update_data['email']}, limit=1) != 0:
            raise HTTPException(
                400,
                "E-Mail address already in use, please use existing account")
        new_mail = update_data['email']
        locale = update_data.get(
            'locale',
            user_data.get('locale',
                          config.oauth2.user.properties['locale'].default))
        if locale is None:
            locale = 'en_us'
        tz = _get_tz(update_data.get('zoneinfo', user_data.get('zoneinfo')))
        del update_data['email']
        if is_new and not no_registration:
            user_data['email'] = new_mail
            user_data['email_verified'] = False
            token_valid_until = int(time.time() +
                                    config.manager.token_valid.registration)
            user_data['registration_token'] = create_token(
                user_data['_id'], token_valid_until)

            async def send_mail():
                await async_send_mail_register(user_data, token_valid_until,
                                               locale, tz)
        elif not is_admin:
            token_valid_until = int(time.time() +
                                    config.manager.token_valid.email_set)
            user_data['email_verification_token'] = create_token(
                new_mail, token_valid_until)
            if is_registering:
                user_data['email'] = new_mail
                user_data['email_verified'] = False

            async def send_mail():
                await async_send_mail_verify(locale, new_mail, user_data,
                                             token_valid_until, tz)
        else:
            user_data['email'] = new_mail
            user_data['email_verified'] = False

    if 'access_tokens' in update_data:
        if not isinstance(update_data['access_tokens'], list):
            raise HTTPException(400, "'access_tokens' must be a list")
        try:
            access_tokens = [
                ValidateAccessToken.validate(val)
                for val in update_data['access_tokens']
            ]
        except ValueError as err:
            raise HTTPException(400, str(err))
        _validate_property_write('access_tokens', is_self, is_admin)
        existing_access_tokens = [
            UserPasswordAccessToken.validate(access_token)
            for access_token in user_data.get('access_tokens', [])
        ]
        existing_access_tokens_by_id = {
            existing_access_token.id: existing_access_token
            for existing_access_token in existing_access_tokens
        }
        new_access_tokens = []
        for access_token in access_tokens:
            if access_token.id is not None:
                store_token = existing_access_tokens_by_id.get(access_token.id)
                if store_token is None:
                    raise HTTPException(400,
                                        f"Invalid token ID {access_token.id}")
                store_token.description = access_token.description
                if access_token.token is not None:
                    store_token.token = access_token.token
            else:
                store_token = UserPasswordAccessToken(
                    id=generate_token(24),
                    description=access_token.description,
                    token=access_token.token,
                )
            new_access_tokens.append(store_token)
        del update_data['access_tokens']
        user_data['access_tokens'] = [
            access_token.dict() for access_token in new_access_tokens
        ]

    if 'groups' in update_data:
        if await _update_groups(
                user_data,
                update_data,
                property='groups',
                is_self=is_self,
                is_admin=is_admin,
                existence_check_property=None,
                groups_add_property='members',
                groups_pull_properties=(
                    'members',
                    'email_allowed_forward_members',
                    'email_forward_members',
                    'email_postbox_access_members',
                ),
                members_pull_properties=(
                    'email_allowed_forward_members',
                    'email_forward_members',
                    'email_postbox_access_members',
                ),
        ):
            reset_user_cache = True

    if 'email_allowed_forward_groups' in update_data:
        await _update_groups(
            user_data,
            update_data,
            property='email_allowed_forward_groups',
            is_self=is_self,
            is_admin=is_admin,
            existence_check_property='groups',
            groups_add_property='email_allowed_forward_members',
            groups_pull_properties=('email_allowed_forward_members',
                                    'email_forward_members'),
            members_pull_properties=('email_forward_members', ))

    if 'email_forward_groups' in update_data:
        await _update_groups(
            user_data,
            update_data,
            property='email_forward_groups',
            is_self=is_self,
            is_admin=is_admin,
            existence_check_property='email_allowed_forward_groups',
            groups_add_property='email_forward_members',
            groups_pull_properties=('email_forward_members', ),
        )

    if 'email_postbox_access_groups' in update_data:
        await _update_groups(
            user_data,
            update_data,
            property='email_postbox_access_groups',
            is_self=is_self,
            is_admin=is_admin,
            existence_check_property='groups',
            groups_add_property='email_postbox_access_members',
            groups_pull_properties=('email_postbox_access_members', ),
        )

    for key, value in update_data.items():
        _validate_property_write(key, is_self, is_admin)
        prop = config.oauth2.user.properties[key]
        if prop.write_once and user_data.get(key) is not None:
            raise HTTPException(400, f"{repr(key)} can only be set once")
        if not value and not prop.required and key in user_data:
            del user_data[key]
        if prop.type in (UserPropertyType.str, UserPropertyType.multistr,
                         UserPropertyType.token):
            if not isinstance(value, str):
                raise HTTPException(
                    400, f"{repr(key)}={repr(value)} is not a string")
            if prop.template is not None:
                raise HTTPException(400,
                                    f"{repr(key)}={repr(value)} is generated")
            if prop.format is not None:
                regex = get_regex(prop.format)
                if not regex.fullmatch(value):
                    raise HTTPException(
                        400,
                        f"{repr(key)}={repr(value)} does not match pattern {repr(regex.pattern)}"
                    )
            user_data[key] = value
        elif prop.type == UserPropertyType.int:
            if isinstance(value, float):
                if not value.is_integer():
                    raise HTTPException(
                        400, f"{repr(key)}={repr(value)} is not an integer")
                value = int(value)
            if not isinstance(value, int):
                raise HTTPException(
                    400, f"{repr(key)}={repr(value)} is not an integer")
            user_data[key] = value
        elif prop.type == UserPropertyType.bool:
            if not isinstance(value, bool):
                raise HTTPException(
                    400, f"{repr(key)}={repr(value)} is not a boolean")
            user_data[key] = value
        elif prop.type == UserPropertyType.datetime:
            if not isinstance(value, str):
                raise HTTPException(
                    400, f"{repr(key)}={repr(value)} is not a datetime string")
            try:
                parse_datetime(value)
                user_data[key] = value
            except ValueError:
                raise HTTPException(
                    400, f"{repr(key)}={repr(value)} is not a datetime string")
        elif prop.type == UserPropertyType.date:
            if not isinstance(value, str):
                raise HTTPException(
                    400, f"{repr(key)}={repr(value)} is not a date string")
            try:
                parse_date(value)
                user_data[key] = value
            except ValueError:
                raise HTTPException(
                    400, f"{repr(key)}={repr(value)} is not a datetime string")
        elif prop.type == UserPropertyType.enum:
            if not isinstance(value, str):
                raise HTTPException(
                    400, f"{repr(key)}={repr(value)} is not a string")
            assert prop.values is not None
            values = [enum_value.value for enum_value in prop.values]
            if value not in values:
                raise HTTPException(
                    400,
                    f"{repr(key)}={repr(value)} is not a valid enum value")
            user_data[key] = value
        else:
            raise NotImplementedError(f"{repr(prop.type)}")

    # Set others to default
    if is_new or is_registering:
        for key, value in config.oauth2.user.properties.items():
            if value.default is not None and key not in user_data:
                user_data[key] = value.default

    # Activate the user after registration
    if is_registering:
        user_data['active'] = True

    # Apply all templates and validate required, when not active
    if user_data.get('active', False):
        # Validate that all required variables are present
        for key, value in config.oauth2.user.properties.items():
            if value.required and user_data.get(key) is None:
                raise HTTPException(400, f"Missing {repr(key)}")
        # Apply templates (they should not be required)
        for key, value in config.oauth2.user.properties.items():
            if (value.type == UserPropertyType.str
                    and value.template is not None
                    and (not value.write_once or not user_data.get(key))):
                assert "'''" not in value.template, f"Invalid ''' in template: {value.template}"
                user_data[key] = eval(
                    f"f'''{value.template}'''",
                    {
                        'make_username': make_username,
                        'config': config
                    },
                    user_data,
                )

    user_data['updated_at'] = int(time.time())

    User.validate(user_data)

    if is_new:
        await async_user_collection.insert_one(user_data)
    else:
        await async_user_collection.replace_one({'_id': user_data['_id']},
                                                user_data)
    if user_data.get('active', False):
        if reset_user_cache:
            await async_client_user_cache_collection.delete_many(
                {'user_id': user_data['_id']})
        else:
            await async_client_user_cache_collection.update_many(
                {'user_id': user_data['_id']},
                {'$set': {
                    'last_modified': user_data['updated_at']
                }},
            )
    elif was_active:
        await async_client_user_cache_collection.delete_many(
            {'user_id': user_data['_id']})
        await async_token_collection.delete_many({'user_id': user_data['_id']})
        await async_session_collection.delete_many(
            {'user_id': user_data['_id']})
        await async_authorization_code_collection.delete_many(
            {'user_id': user_data['_id']})
    elif reset_user_cache:
        await async_client_user_cache_collection.delete_many(
            {'user_id': user_data['_id']})
    # Last: Send the email if there is one
    await send_mail()
Exemplo n.º 29
0
def datetime_verifier(raw_data):
    return parse_datetime(raw_data)
Exemplo n.º 30
0
 def validate(cls, v: Any) -> datetime:
     return datetime.fromtimestamp(parse_datetime(v).timestamp())