Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# --- Python junk ---
__pycache__/
*.pyc
*.pyo
*.pyd

# --- Virtual environments ---
.venv/
venv/
env/
.env/

logs

# --- Build and dependency artifacts ---
dist/
build/
*.egg-info/
.eggs/
.cache/
*.log

.flake8

# --- Development / tooling directories ---
.idea/
.vscode/
tests/
docs/
.git/
.gitignore

# --- OS & editor clutter ---
.DS_Store
Thumbs.db

# --- Node / frontend (if present) ---
node_modules/
npm-debug.log
yarn.lock

# --- Docker ---
Dockerfile*
compose*.yml
docker-compose*.yml

# --- Local data ---
*.db
*.sqlite3

/docker
!/docker/entrypoint.sh
5 changes: 5 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[flake8]
extend-ignore = E501 E712
exclude = .git,__pycache__,.venv,old,build,dist,trimet.asset.egg-info
max-complexity = 30
max-line-length = 88
80 changes: 80 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: CI

on:
push:
branches: ["**"] # runs on any branch

jobs:
test:
name: Run Tests
runs-on: ubuntu-latest

env:
POETRY_VIRTUALENVS_CREATE: false
POETRY_NO_INTERACTION: 1
PYTHONUNBUFFERED: 1
PYTHONDONTWRITEBYTECODE: 1

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install Poetry
run: |
curl -sSL https://install.python-poetry.org | python3 -
echo "$HOME/.local/bin" >> $GITHUB_PATH

- name: Install dependencies
run: |
poetry install --with dev --no-interaction --no-ansi

- name: Run tests
run: |
poetry run black . --check
poetry run pytest --cov=com -v -s --log-cli-level=DEBUG
env:
PYTHONPATH: ${{ github.workspace }}

build:
name: Build Docker Image
runs-on: ubuntu-latest
needs: test # only build if tests pass

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract branch name
id: extract_branch
run: |
# Strip refs/heads/ from the branch name
BRANCH_NAME=${GITHUB_REF#refs/heads/}
# Replace slashes with dashes for Docker tag safety
SAFE_BRANCH_NAME=${BRANCH_NAME//\//-}
echo "branch=$SAFE_BRANCH_NAME" >> $GITHUB_OUTPUT

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
push: true
tags: |
ghcr.io/opentransittools/pelias-adapter:${{ steps.extract_branch.outputs.branch }}
build-args: |
POETRY_VERSION=2.2.1
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ develop-eggs
lib
lib64

.venv

# Installer logs
pip-log.txt

Expand Down
3 changes: 0 additions & 3 deletions CHANGES.txt

This file was deleted.

65 changes: 24 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,24 @@
Pelias.Adapter
==============

Python wrapper to make Pelias look like SOLR (geosearch instance)


build:
------
1. install python 3.x, along easy_install, zc.buildout ("zc.buildout==1.5.2") and git
1. git clone https://github.com/OpenTransitTools/pelias.adapter.git
1. cd pelias.adapter
1. buildout

run:
----
1. rm nohup.out; nohup bin/pserve config/development.ini --reload PELIAS_SOLR=1 &
1. http://localhost:45454/solr/select?q=2
1. http://localhost:45454/solr/boundary/select?q=8

test:
-----
1. run the server (see above)
1. bin/test

rules:
-----
1. try 'autocomplete' .. if that fails, try 'search'
1. (or should we look at length of string and try 'search' first on longer strings?)
1. fix 'same string' problem:
- remove duplicate points (strings 99% similar and lat/lon very close by)
- clean up duplicate strings (e.g., Starbucks problem)
1. call Pelias with configurable url (sources=oa,osm,transit, etc...)
1. if we get WoF a city record(s), then strip city from query string and resubmit (e.g., bad city problem)
1. ...

urls:
-----
1. Pelias TriMet-only: https://ws.trimet.org/peliaswrap/v1/autocomplete?text=6
1 Pelias Multi-Agency: https://ws.trimet.org/peliaswrap/v1/rtp/autocomplete?text=6
1. SOLR: https://ws-st.trimet.org/solrwrap/v1/select?start=0&limit=10&wt=json&qt=dismax&rows=10&q=834%20SE&fq=type:stop
1. SOLR Stops Only: https://ws-st.trimet.org/solrwrap/v1/select?start=0&limit=10&wt=json&qt=dismax&rows=10&q=834%20SE&fq=type:stop
# Pelias Adapter API

A FastAPI-based web API for Open Transit Tools, providing Pelias geocoding services.

## Features

- FastAPI web server
- Pelias geocoding endpoints
- CORS support
- Configurable logging

## Requirements

- Python 3.12+
- Dependencies listed in `pyproject.toml`

## Installation

Clone the repository and install dependencies using Poetry:

```sh
git clone https://github.com/OpenTransitTools/pelias.adapter.git
cd pelias.adapter
poetry install
29 changes: 0 additions & 29 deletions buildout.cfg

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions com/github/ott/pelias/adapter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# FastAPI Oracle Template Application
1 change: 1 addition & 0 deletions com/github/ott/pelias/adapter/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Core configuration and settings
43 changes: 43 additions & 0 deletions com/github/ott/pelias/adapter/core/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import os
from logging import getLogger
from zoneinfo import ZoneInfo

from ott.utils.svr.pyramid.globals import CACHE_LONG
from pydantic import ConfigDict
from pydantic_settings import BaseSettings

logger = getLogger(__name__)

ENVIRONMENT = os.getenv("ENVIRONMENT", "dev") # default to dev

CACHE_LONG_STR = f"public, max-age={CACHE_LONG}"

logger.info(f"Environment: {ENVIRONMENT}")

ENV_FILE = f"{ENVIRONMENT}.env"

TIME_ZONE: ZoneInfo = ZoneInfo("America/Los_Angeles")

DATE_STRING_FORMAT = "%Y-%m-%d %H:%M:%S %Z%z"


class Settings(BaseSettings):
"""
Application settings with Oracle database configuration
"""

# Application settings
app_name: str = "FastAPI Trimet Pelias Adapter"
debug: bool = False

model_config = ConfigDict(
env_file=ENV_FILE, # or f"{ENVIRONMENT}.env"
case_sensitive=False,
extra="allow",
)


# Global settings instance
settings = Settings()

logger.info(f"Loaded settings for environment: {ENVIRONMENT}, {settings.debug}")
9 changes: 9 additions & 0 deletions com/github/ott/pelias/adapter/core/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class PeliasAdapterError(Exception):
"""Base class for all Pelias Adapter exceptions."""

def __init__(self, message):
super().__init__(message)
self.message = message

def __str__(self):
return self.message
1 change: 1 addition & 0 deletions com/github/ott/pelias/adapter/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# SQLAlchemy models
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from ott.utils.dao.base import MinimalDao
import logging

from datetime import datetime

from ott.utils import geo_utils
from ott.utils.dao.base import MinimalDao

import logging
log = logging.getLogger(__file__)
from com.github.ott.pelias.adapter.core.config import TIME_ZONE, DATE_STRING_FORMAT

log = logging.getLogger(__file__)

"""
TODO: have an endpoint /solrwrap/boundary?text=834 SE that calculates the bounds
Expand All @@ -13,29 +17,29 @@
https://maps.trimet.org/solr/select?_dc=1618955956672&start=0&limit=10&fq=(-type%3A26%20AND%20-type%3Aroute)&wt=json&qt=dismax&rows=10&q=44%20se
"""


class SolrRecord(MinimalDao):
"""
:see: https://trimet.org/solr/select?q=3&rows=6&wt=json&fq=
"""

def __init__(self):
super(SolrRecord, self).__init__()
super().__init__()
self.id = ""
self.type = ""
self.type_name = ""
self.vtype = "1"
self.name = ""

self.city = ""
self.county = ""
self.neighborhood = ""
self.zip_code = ""

self.x = 7645053.5
self.y = 684388.9
self.lon = -122.67371
self.lat = 45.523335
self.timestamp = datetime.now(TIME_ZONE).strftime(DATE_STRING_FORMAT)

self.timestamp = "2018-02-03T07:47:45.045Z"
self.score = 0.0

def set_value(self, name, val):
Expand All @@ -44,19 +48,19 @@ def set_value(self, name, val):
def parse_pelias(self, json):
try:
# step 1: parse props
properties = json.get('properties')
self.id = properties.get('id')
self.type = properties.get('layer')
properties = json.get("properties")
self.id = properties.get("id")
self.type = properties.get("layer")
self.type_name = "Address"

self.name = properties.get('name', "")
self.city = properties.get('locality', "")
self.neighborhood = properties.get('neighborhood', "")
self.county = properties.get('county', "")
self.score = properties.get('confidence', 0.1)
self.name = properties.get("name", "")
self.city = properties.get("locality", "")
self.neighborhood = properties.get("neighborhood", "")
Comment thread
alfrice marked this conversation as resolved.
self.county = properties.get("county", "")
self.score = properties.get("confidence", 0.1)

# step 2: parse / calculate geometry
geojson = json.get('geometry')
geojson = json.get("geometry")
lon, lat = self.parse_geojson(geojson)
self.lon = lon
self.lat = lat
Expand Down
Loading