initial commit
This commit is contained in:
commit
1253955070
75 changed files with 13972 additions and 0 deletions
166
api/internal/domain/models.go
Normal file
166
api/internal/domain/models.go
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
// Package domain contains the core entities of the application. It has no
|
||||
// dependencies on transport, storage or database layers so that the business
|
||||
// rules can be reasoned about in isolation.
|
||||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
// User represents a logged-in user. Identity is owned by the external go-loose
|
||||
// SSO service; this service only stores the identifier it asserts.
|
||||
type User struct {
|
||||
ID string `json:"id" gql:"id"`
|
||||
Username string `json:"username" gql:"username"`
|
||||
Email string `json:"email" gql:"email"`
|
||||
}
|
||||
|
||||
// Coordinate is a WGS-84 geographic point.
|
||||
type Coordinate struct {
|
||||
Latitude float64 `json:"latitude" gql:"latitude"`
|
||||
Longitude float64 `json:"longitude" gql:"longitude"`
|
||||
}
|
||||
|
||||
// Valid reports whether the coordinate is a real point on Earth.
|
||||
func (c Coordinate) Valid() bool {
|
||||
if c.Latitude < -90 || c.Latitude > 90 {
|
||||
return false
|
||||
}
|
||||
return c.Longitude >= -180 && c.Longitude <= 180
|
||||
}
|
||||
|
||||
// AnalysisStatus tracks the progress of the image analysis pipeline. Analysis
|
||||
// runs out of band, so a picture is visible to the API before its badges exist.
|
||||
type AnalysisStatus string
|
||||
|
||||
const (
|
||||
AnalysisPending AnalysisStatus = "pending"
|
||||
AnalysisCompleted AnalysisStatus = "completed"
|
||||
AnalysisFailed AnalysisStatus = "failed"
|
||||
)
|
||||
|
||||
// Badge is a recognisable attribute detected in a picture, for example an
|
||||
// object ("dog"), a scene ("beach") or a property ("low-light").
|
||||
type Badge struct {
|
||||
// Label is the stable machine-readable name, e.g. "dog" or "beach".
|
||||
Label string `json:"label" gql:"label"`
|
||||
// Display is a human-friendly rendering of the label, e.g. "Dog".
|
||||
Display string `json:"display" gql:"display"`
|
||||
// Confidence is a 0..1 score. Heuristic badges tend to be lower confidence
|
||||
// than those returned by a trained model.
|
||||
Confidence float64 `json:"confidence" gql:"confidence"`
|
||||
// Source records which analyzer produced the badge, which lets the UI
|
||||
// distinguish machine-derived facts from heuristics.
|
||||
Source string `json:"source" gql:"source"`
|
||||
}
|
||||
|
||||
// Image is a picture attached to a Place.
|
||||
type Image struct {
|
||||
ID string `json:"id" gql:"id"`
|
||||
PlaceID string `json:"placeId" gql:"placeId"`
|
||||
// StorageKey is the location of the bytes inside the blob store. It is not
|
||||
// exposed to clients; URL is the public rendering of this key.
|
||||
StorageKey string `json:"-" gql:"-"`
|
||||
URL string `json:"url" gql:"url"`
|
||||
Caption string `json:"caption" gql:"caption"`
|
||||
ContentType string `json:"contentType" gql:"contentType"`
|
||||
ByteSize int64 `json:"byteSize" gql:"byteSize"`
|
||||
Width int `json:"width" gql:"width"`
|
||||
Height int `json:"height" gql:"height"`
|
||||
|
||||
// Coordinate is where the picture was taken. It is taken from the EXIF GPS
|
||||
// tag when present, and falls back to the parent Place otherwise.
|
||||
Coordinate *Coordinate `json:"coordinate" gql:"coordinate"`
|
||||
// TakenAt is when the picture was captured, from the EXIF timestamp.
|
||||
TakenAt *time.Time `json:"takenAt" gql:"takenAt"`
|
||||
// CameraMake and CameraModel come from EXIF and identify the capture device.
|
||||
CameraMake string `json:"cameraMake" gql:"cameraMake"`
|
||||
CameraModel string `json:"cameraModel" gql:"cameraModel"`
|
||||
|
||||
Badges []Badge `json:"badges" gql:"badges"`
|
||||
AnalysisStatus AnalysisStatus `json:"analysisStatus" gql:"analysisStatus"`
|
||||
AnalysisError string `json:"analysisError" gql:"analysisError"`
|
||||
|
||||
// LinkedImages are other pictures taken at the same location, resolved by
|
||||
// the linking pass rather than stored.
|
||||
LinkedImages []Image `json:"linkedImages" gql:"linkedImages"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt" gql:"createdAt"`
|
||||
}
|
||||
|
||||
// Place is a location the user visited and shared. Pictures and a free-text
|
||||
// description hang off it, and it is the anchor for geographic linking.
|
||||
type Place struct {
|
||||
ID string `json:"id" gql:"id"`
|
||||
Title string `json:"title" gql:"title"`
|
||||
Description string `json:"description" gql:"description"`
|
||||
|
||||
// Coordinate is the location of the place. Required for linking to work;
|
||||
// when a picture carries its own EXIF GPS tag that value takes precedence.
|
||||
Coordinate *Coordinate `json:"coordinate" gql:"coordinate"`
|
||||
// LinkRadiusMeters is how close two pictures must be to count as having
|
||||
// been taken at the same place.
|
||||
LinkRadiusMeters float64 `json:"linkRadiusMeters" gql:"linkRadiusMeters"`
|
||||
|
||||
Creator User `json:"creator" gql:"creator"`
|
||||
Images []Image `json:"images" gql:"images"`
|
||||
Comments []Comment `json:"comments" gql:"comments"`
|
||||
// RelatedPlaces are other Places whose pictures overlap this one.
|
||||
RelatedPlaces []RelatedPlace `json:"relatedPlaces" gql:"relatedPlaces"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt" gql:"createdAt"`
|
||||
}
|
||||
|
||||
// RelatedPlace records the link between two Places discovered by the linking
|
||||
// pass, along with the evidence for it.
|
||||
type RelatedPlace struct {
|
||||
Place Place `json:"place" gql:"place"`
|
||||
DistanceMeters float64 `json:"distanceMeters" gql:"distanceMeters"`
|
||||
SharedImageIDs []string `json:"sharedImageIds" gql:"sharedImageIds"`
|
||||
}
|
||||
|
||||
// Link is a single edge produced by the linking pass, connecting one picture to
|
||||
// another that was taken at the same place.
|
||||
type Link struct {
|
||||
ImageID string `json:"imageId" gql:"imageId"`
|
||||
RelatedImageID string `json:"relatedImageId" gql:"relatedImageId"`
|
||||
// DistanceMeters is the great-circle distance between the two capture points.
|
||||
DistanceMeters float64 `json:"distanceMeters" gql:"distanceMeters"`
|
||||
// SamePlace is true when the two pictures belong to the same Place record.
|
||||
SamePlace bool `json:"samePlace" gql:"samePlace"`
|
||||
// Reason explains the evidence, e.g. "same-place" or "within-radius".
|
||||
Reason string `json:"reason" gql:"reason"`
|
||||
}
|
||||
|
||||
// Comment is a user reaction on a Place.
|
||||
type Comment struct {
|
||||
ID string `json:"id" gql:"id"`
|
||||
PlaceID string `json:"placeId" gql:"placeId"`
|
||||
Content string `json:"content" gql:"content"`
|
||||
Creator User `json:"creator" gql:"creator"`
|
||||
CreatedAt time.Time `json:"createdAt" gql:"createdAt"`
|
||||
}
|
||||
|
||||
// PlaceInput is the payload for creating a Place. Latitude and Longitude are
|
||||
// pointers so that an omitted coordinate can be told apart from the origin,
|
||||
// which is a legitimate coordinate.
|
||||
type PlaceInput struct {
|
||||
Title string `json:"title" gql:"title"`
|
||||
Description string `json:"description" gql:"description"`
|
||||
Latitude *float64 `json:"latitude" gql:"latitude"`
|
||||
Longitude *float64 `json:"longitude" gql:"longitude"`
|
||||
LinkRadiusMeters *float64 `json:"linkRadiusMeters" gql:"linkRadiusMeters"`
|
||||
// Caption is the description of the first picture in the upload, if any.
|
||||
Caption string `json:"caption" gql:"caption"`
|
||||
}
|
||||
|
||||
// Coordinate builds a Coordinate from the optional latitude and longitude,
|
||||
// returning nil when they are absent or do not form a valid point.
|
||||
func (p PlaceInput) Coordinate() *Coordinate {
|
||||
if p.Latitude == nil || p.Longitude == nil {
|
||||
return nil
|
||||
}
|
||||
c := Coordinate{Latitude: *p.Latitude, Longitude: *p.Longitude}
|
||||
if !c.Valid() {
|
||||
return nil
|
||||
}
|
||||
return &c
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue