Esempio n. 1
0
def _get_opening_dates(site: dict) -> location.OpenDate:
    opens = _parse_datetime(site["dateFrom"])
    closes = _parse_datetime(site["dateTo"])
    if opens:
        opens = opens.date().isoformat()
    if closes:
        closes = closes.date().isoformat()
    return [location.OpenDate(opens=opens, closes=closes)]
Esempio n. 2
0
def _get_opening_dates(site: dict) -> Optional[List[schema.OpenDate]]:
    start_date = site["attributes"]["StartDate"]
    end_date = site["attributes"]["EndDate"]

    if start_date or end_date:
        return [schema.OpenDate(opens=start_date, closes=end_date)]

    return None
Esempio n. 3
0
def _make_opening_dates_contiguous(
        dates: List[datetime]) -> List[schema.OpenDate]:
    """
    Converts an list of `dates` into an equivalent list of inclusive `OpenDate` ranges.

    For example, `[2021-06-01, 2021-06-02, 2021-06-04]` becomes
    `[{opens: 2021-06-01, closes: 2021-06-02}, {opens: 2021-06-04, closes: 2021-06-04}]`.
    """
    dates.sort()
    ranges: List[schema.OpenDate] = []
    current_opens: Optional[datetime] = None
    current_closes: Optional[datetime] = None
    # Invariants:
    # - current_range.{opens,closes} are None at the beginning, and non-None otherwise.
    # - At iteration i (date = dates[i]):
    #   dates[0..i-1] = concat([r.opens..r.closes] for r in ranges) + [current_opens..current_closes]
    #   Here [ ] denote inclusive ranges on both ends.
    for date in dates:
        if not (current_opens and current_closes):
            # First date seen. Start with a singleton range.
            current_opens = date
            current_closes = date
        else:
            # Compute the difference in days between this new date and the end date of the current range.
            delta = (date - current_closes).days
            if delta == 0:
                # Same date seen twice in succession.
                # Unlikely, but handle it anyway by changing nothing.
                logger.debug("Encountered same date twice in succession: %s",
                             date)
            elif delta == 1:
                # Extend the current range by one day to include this date.
                current_closes = date
            else:
                # There is a gap of at least 1 day between the current range and this date.
                # Save the current range.
                ranges.append(
                    schema.OpenDate(opens=current_opens,
                                    closes=current_closes))
                # And start a new one.
                current_opens = date
                current_closes = date
    # Include the last range.
    ranges.append(schema.OpenDate(opens=current_opens, closes=current_closes))
    return ranges
Esempio n. 4
0
def _get_opening_dates(site: dict):
    if site["Start Date"] == "" and site["End Date"]:
        return None
    return [
        schema.OpenDate(
            opens=_normalize_date(site["Start Date"]),
            closes=_normalize_date(site["End Date"]),
        )
    ]
Esempio n. 5
0
def _get_opening_dates(site: dict) -> List[schema.OpenDate]:
    date = site["date"]
    date_split = date.split("/")

    return [
        schema.OpenDate(
            opens=f"{date_split[2]}-{date_split[0]}-{date_split[1]}",
            closes=f"{date_split[2]}-{date_split[0]}-{date_split[1]}",
        )
    ]
Esempio n. 6
0
def _get_opening_dates(site: dict) -> Optional[List[schema.OpenDate]]:

    start_date = site["attributes"].get("opendate")

    if start_date:
        start_date = datetime.datetime.fromtimestamp(start_date / 1000)

    if start_date:
        return [schema.OpenDate(opens=start_date, closes=None)]
    else:
        return None
Esempio n. 7
0
def _get_opening_dates(site: dict) -> Optional[List[schema.OpenDate]]:
    opens = None
    closes = None
    if site["attributes"]["begindate"] is not None:
        opens = (datetime.datetime.fromtimestamp(
            site["attributes"]["begindate"] // 1000).date().isoformat())

    if site["attributes"]["enddate"] is not None:
        closes = (datetime.datetime.fromtimestamp(
            site["attributes"]["enddate"] // 1000).date().isoformat())

    if opens is None and closes is None:
        return None

    return [schema.OpenDate(
        opens=opens,
        closes=closes,
    )]
def test_opening_days():
    assert location.OpenDate(
        opens="2021-04-01",
        closes="2021-04-01",
    )

    assert location.OpenDate(opens="2021-04-01")

    assert location.OpenDate(closes="2021-04-01", )

    with pytest.raises(pydantic.error_wrappers.ValidationError):
        location.OpenDate(closes="2021-04-01T04:04:04", )

    with pytest.raises(pydantic.error_wrappers.ValidationError):
        location.OpenDate(opens="tomorrow", )

    with pytest.raises(pydantic.error_wrappers.ValidationError):
        location.OpenDate(
            opens="2021-06-01",
            closes="2021-01-01",
        )
def _get_opening_dates(site: dict) -> Optional[List[schema.OpenDate]]:
    """
    "May 3 - May 6 and May 10 - May 13 \n9am-1pm \nMay 24 - May 27 \n2pm - 7pm"
    "May 3-May 6 and May 10-May13 \n9am-1pm \nMay 24-May 27 \n2pm-7pm"
    "May 5-May 8: 9am-1pm \nMay 12-15, 19-22, 26-29: 2pm-7pm"
    "May 3: 9am-1pm \nMay 6-8, 10, 13-15, 17, 20-22, 24: 2pm-7pm \nMay 27-29: 9am-1pm"
    "May 5, 6, 12, 14, 19, 20 \n9am-2pm"
    """
    opening_dates = []
    days_hours = site["Normal Days / Hours"].lower().replace("and", ", ")

    # short-circuit if it's an entry with days, not dates
    if not re.search(MONTHS_PATTERN, days_hours):
        return None

    # split on the "9am-1pm"s (leading ":" is optional, giving us just the
    # date entries
    entries = re.split(
        fr"(?::\s*)?(?:{HOURS_PATTERN})\s*(?:{AM_PM_PATTERN})\s*-\s*(?:{HOURS_PATTERN})\s*(?:{AM_PM_PATTERN})",
        days_hours,
    )
    for entry in entries:
        entry_match = re.search(MONTH_DATE_TO_MONTH_DATE_PATTERN, entry)
        if entry_match:
            for start_date, end_date in re.findall(
                    MONTH_DATE_TO_MONTH_DATE_PATTERN, entry):
                # "May 5-May 8"
                start_date = dateparser.parse(start_date).date().isoformat()
                end_date = dateparser.parse(end_date).date().isoformat()
                opening_dates.append(
                    schema.OpenDate(opens=start_date, closes=end_date))
            continue

        entry_match = re.search(
            fr"(?P<month>({MONTHS_PATTERN}))\s+(?P<dates>({DATE_SINGLE_OR_RANGE_PATTERN})(\s*,\s*({DATE_SINGLE_OR_RANGE_PATTERN}))*)",
            entry,
        )
        if entry_match:
            # "May 6-8, 10, 13-15, 17, 20-22, 24"
            # "May 11"
            month = entry_match.group("month")
            pieces = entry_match.group("dates")
            for piece in re.split(r"\s*,\s*", pieces):
                if "-" in piece:
                    start, end = re.split(r"\s*-\s*", piece)
                    start_date = dateparser.parse(
                        f"{month} {start}").date().isoformat()
                    end_date = dateparser.parse(
                        f"{month} {end}").date().isoformat()
                    opening_dates.append(
                        schema.OpenDate(opens=start_date, closes=end_date))
                else:
                    date = dateparser.parse(
                        f"{month} {piece}").date().isoformat()
                    opening_dates.append(
                        schema.OpenDate(opens=date, closes=date))
            continue

        if entry:
            logger.info(f"Unparseable opening_dates: {entry}")

    return opening_dates or None
Esempio n. 10
0
def full_location():
    return location.NormalizedLocation(
        id="source:dad13365-2202-4dea-9b37-535288b524fe",
        name="Rite Aid Pharmacy 5952",
        address=location.Address(
            street1="1991 Mountain Boulevard",
            city="Oakland",
            state="CA",
            zip="94611",
        ),
        location=location.LatLng(
            latitude=37.8273167,
            longitude=-122.2105179,
        ),
        contact=[
            location.Contact(
                contact_type=location.ContactType.BOOKING,
                phone="(916) 445-2841",
            ),
            location.Contact(
                contact_type=location.ContactType.GENERAL,
                phone="(510) 339-2215",
            ),
        ],
        languages=["en"],
        opening_dates=[
            location.OpenDate(
                opens="2021-04-01",
                closes="2021-04-01",
            ),
        ],
        opening_hours=[
            location.OpenHour(
                day=location.DayOfWeek.MONDAY,
                opens="08:00",
                closes="14:00",
            ),
        ],
        availability=location.Availability(
            drop_in=False,
            appointments=True,
        ),
        inventory=[
            location.Vaccine(
                vaccine=location.VaccineType.MODERNA,
                supply_level=location.VaccineSupply.IN_STOCK,
            ),
        ],
        access=location.Access(
            walk=True,
            drive=False,
            wheelchair=location.WheelchairAccessLevel.PARTIAL,
        ),
        parent_organization=location.Organization(
            id=location.VaccineProvider.RITE_AID,
            name="Rite Aid Pharmacy",
        ),
        links=[
            location.Link(
                authority=location.LocationAuthority.GOOGLE_PLACES,
                id="ChIJA0MOOYWHj4ARW8M-vrC9yGA",
            ),
        ],
        notes=["long note goes here"],
        active=True,
        source=location.Source(
            source="source",
            id="dad13365-2202-4dea-9b37-535288b524fe",
            fetched_from_uri="https://example.org",
            fetched_at="2020-04-04T04:04:04.4444",
            published_at="2020-04-04T04:04:04.4444",
            data={"id": "dad13365-2202-4dea-9b37-535288b524fe"},
        ),
    )
def test_valid_location():
    # Minimal record
    assert location.NormalizedLocation(
        id="source:id",
        source=location.Source(
            source="source",
            id="id",
            data={"id": "id"},
        ),
    )

    # Full record with str enums
    full_loc = location.NormalizedLocation(
        id="source:id",
        name="name",
        address=location.Address(
            street1="1991 Mountain Boulevard",
            street2="#1",
            city="Oakland",
            state="CA",
            zip="94611",
        ),
        location=location.LatLng(
            latitude=37.8273167,
            longitude=-122.2105179,
        ),
        contact=[
            location.Contact(
                contact_type="booking",
                phone="(916) 445-2841",
            )
        ],
        languages=["en"],
        opening_dates=[
            location.OpenDate(
                opens="2021-04-01",
                closes="2021-04-01",
            ),
        ],
        opening_hours=[
            location.OpenHour(
                day="monday",
                opens="08:00",
                closes="14:00",
            ),
        ],
        availability=location.Availability(
            drop_in=False,
            appointments=True,
        ),
        inventory=[
            location.Vaccine(
                vaccine="moderna",
                supply_level="in_stock",
            ),
        ],
        access=location.Access(
            walk=True,
            drive=False,
            wheelchair="partial",
        ),
        parent_organization=location.Organization(
            id="rite_aid",
            name="Rite Aid",
        ),
        links=[
            location.Link(
                authority="google_places",
                id="abc123",
            ),
        ],
        notes=["note"],
        active=True,
        source=location.Source(
            source="source",
            id="id",
            fetched_from_uri="https://example.org",
            fetched_at="2020-04-04T04:04:04.4444",
            published_at="2020-04-04T04:04:04.4444",
            data={"id": "id"},
        ),
    )
    assert full_loc

    # Verify dict serde
    full_loc_dict = full_loc.dict()
    assert full_loc_dict

    parsed_full_loc = location.NormalizedLocation.parse_obj(full_loc_dict)
    assert parsed_full_loc

    assert parsed_full_loc == full_loc

    # Verify json serde
    full_loc_json = full_loc.json()
    assert full_loc_json

    parsed_full_loc = location.NormalizedLocation.parse_raw(full_loc_json)
    assert parsed_full_loc

    assert parsed_full_loc == full_loc

    # Verify dict->json serde
    full_loc_json_dumps = json.dumps(full_loc_dict)
    assert full_loc_json_dumps

    assert full_loc_json_dumps == full_loc_json

    parsed_full_loc = location.NormalizedLocation.parse_raw(
        full_loc_json_dumps)
    assert parsed_full_loc

    assert parsed_full_loc == full_loc