Compare commits
57 Commits
v0.1
...
fix/modern
Author | SHA1 | Date |
---|---|---|
Cadey Ratio | a98f5fcd3a | |
Cadey Ratio | 0a2daa0140 | |
Cadey Ratio | b0dc06c75a | |
Christine Dodrill | 0575283f5c | |
Christine Dodrill | 5e6f321c9c | |
Christine Dodrill | bb55555bdd | |
Christine Dodrill | a0fd4d1588 | |
Christine Dodrill | 95cb346e20 | |
Christine Dodrill | b0bd424056 | |
Christine Dodrill | d7a5ccb2cb | |
Christine Dodrill | b4ec8adf9c | |
Christine Dodrill | 8e92e1097e | |
Christine Dodrill | f4bf5ebe0b | |
Christine Dodrill | e47a4da840 | |
Christine Dodrill | 3e27f55a76 | |
Christine Dodrill | 895f8011a7 | |
Christine Dodrill | 5472c5c969 | |
Christine Dodrill | 6b7b1c78d1 | |
Christine Dodrill | 77ba95c811 | |
Christine Dodrill | 92def355ff | |
Christine Dodrill | af1521874d | |
Christine Dodrill | d668137e95 | |
Christine Dodrill | 2982eb2bd5 | |
Christine Dodrill | 375278bc12 | |
Christine Dodrill | c050b65bbf | |
Christine Dodrill | 882b5b1551 | |
Christine Dodrill | 791b7d59a0 | |
Christine Dodrill | 50a6d7b5f6 | |
Christine Dodrill | dc24fa6b48 | |
Christine Dodrill | 5d3a94bffa | |
Christine Dodrill | a0688e4b55 | |
Christine Dodrill | 6aa9ec5b18 | |
Justin Kaufman | 8dfbfd2f2b | |
Christine Dodrill | 0b3e015482 | |
Christine Dodrill | 50c2cbed2c | |
Justin Kaufman | 20cad274e6 | |
Christine Dodrill | 956d57a9dd | |
Christine Dodrill | 34682afd1e | |
Christine Dodrill | e2fd239e37 | |
Christine Dodrill | 0cc73a2e95 | |
Christine Dodrill | 045f69d53a | |
Christine Dodrill | b5d36da2b6 | |
Christine Dodrill | 24ef6f4c69 | |
Christine Dodrill | 67c484ee19 | |
Christine Dodrill | d80166a420 | |
Christine Dodrill | 813e1164c1 | |
Christine Dodrill | 9489789334 | |
Christine Dodrill | c7af9e9bb2 | |
Christine Dodrill | 577de316ac | |
Christine Dodrill | 3ce8c3b833 | |
Christine Dodrill | 97dbe29bca | |
Christine Dodrill | d75622b505 | |
Christine Dodrill | e62d6877ad | |
Christine Dodrill | 651e0f6065 | |
Christine Dodrill | a3ea1ed922 | |
Christine Dodrill | 162052d17b | |
Christine Dodrill | 71489f4f33 |
|
@ -1 +1,2 @@
|
|||
static/* linguist-vendored
|
||||
public/static/* linguist-vendored
|
||||
client/* linguist-vendored
|
||||
|
|
|
@ -59,3 +59,5 @@ docs/_build/
|
|||
# PyBuilder
|
||||
target/
|
||||
|
||||
ponyapi
|
||||
nimcache
|
||||
|
|
17
Dockerfile
17
Dockerfile
|
@ -1,14 +1,17 @@
|
|||
FROM python:2.7.10
|
||||
FROM coopernurse/docker-nim
|
||||
|
||||
RUN adduser --disabled-password --gecos '' r
|
||||
ADD ./requirements.txt /app/requirements.txt
|
||||
WORKDIR /app
|
||||
RUN apk update && apk add bash
|
||||
|
||||
RUN pip install -r ./requirements.txt
|
||||
EXPOSE 5000
|
||||
RUN adduser -D -g '' r
|
||||
RUN chmod a+x /opt/Nim/bin/nim
|
||||
|
||||
ADD . /app
|
||||
|
||||
EXPOSE 5000
|
||||
WORKDIR /app
|
||||
RUN nimble update &&\
|
||||
yes | nimble install &&\
|
||||
nim c -d:release --deadCodeElim:on ponyapi
|
||||
|
||||
USER r
|
||||
CMD gunicorn ponyapi:app --log-file=- -b 0.0.0.0:5000 -w 4
|
||||
CMD ./ponyapi
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
build:
|
||||
nim c --deadcodeElim:on --hints:off ponyapi
|
||||
|
||||
run: build
|
||||
./ponyapi
|
49
README.md
49
README.md
|
@ -35,15 +35,19 @@ Clients
|
|||
- [Go](https://godoc.org/github.com/Xe/PonyAPI/client/go)
|
||||
- [Nim](https://github.com/Xe/PonyAPI/blob/master/client/nim/ponyapi.nim) [Docs](http://ponyapi.apps.xeserv.us/static/nim.html)
|
||||
- [Python](https://github.com/Xe/PonyAPI/blob/master/client/python/ponyapi.py)
|
||||
- [Java](https://github.com/Xe/PonyAPI/tree/master/client/java)
|
||||
|
||||
Routes
|
||||
------
|
||||
|
||||
The canonical route base for PonyAPI is http://ponyapi.apps.xeserv.us. Example
|
||||
usage:
|
||||
The canonical route base for PonyAPI is `https://ponyapi.apps.xeserv.us`. This
|
||||
now supports HTTP/2.0 using [Caddy](https://caddyserver.com) and SSL using
|
||||
[Let's Encrypt](https://letsencrypt.org/). If you get SSL errors, please be
|
||||
sure your system certificate lists are up to date.
|
||||
Example usage:
|
||||
|
||||
```console
|
||||
$ curl http://ponyapi.apps.xeserv.us/season/1/episode/1
|
||||
$ curl https://ponyapi.apps.xeserv.us/season/1/episode/1
|
||||
{
|
||||
"episode": {
|
||||
"air_date": 1286735400,
|
||||
|
@ -55,6 +59,31 @@ $ curl http://ponyapi.apps.xeserv.us/season/1/episode/1
|
|||
}
|
||||
```
|
||||
|
||||
Bare Replies
|
||||
------------
|
||||
|
||||
As of [882b5b1](https://github.com/Xe/PonyAPI/commit/882b5b155157d3a3c9e329fffcf7ff3fdf64d4ee),
|
||||
PonyAPI will accept an `X-API-Options` header that when set to `bare` will
|
||||
return the API replies without the `episode` or `episodes` header.
|
||||
Functionality is otherwise unchanged, however an error will still be shown if
|
||||
something goes wrong, and that will parse differently. This API will return
|
||||
`200` if and **only** if everything went to plan.
|
||||
|
||||
An example:
|
||||
|
||||
```console
|
||||
$ curl --header "X-API-Options: bare" https://ponyapi.apps.xeserv.us/last_aired
|
||||
{
|
||||
"name": "Do Princesses Dream of Magic Sheep?",
|
||||
"air_date": 1436628600,
|
||||
"season": 5,
|
||||
"episode": 13,
|
||||
"is_movie": false
|
||||
}
|
||||
```
|
||||
|
||||
This will also be triggered if you set the query parameter `options` to `bare`.
|
||||
|
||||
### `/all`
|
||||
|
||||
Returns all information about all episodes. This returns an array of Episode
|
||||
|
@ -64,13 +93,18 @@ objects as defined above.
|
|||
|
||||
Returns the episode of My Little Pony: Friendship is Magic that will air next.
|
||||
|
||||
### `/last_aired`
|
||||
|
||||
Returns the episode of My Little Pony: Friendship is Magic that most recently
|
||||
aired.
|
||||
|
||||
### `/season/<number>`
|
||||
|
||||
Returns all information about episodes in the given season number or a `404`
|
||||
reply if no episodes could be found. To get all information about the movies
|
||||
shown, set the season as `99`.
|
||||
|
||||
### `/season/<number>/episode/<number`
|
||||
### `/season/<number>/episode/<number>`
|
||||
|
||||
Returns all information about the episode with the given season and episode
|
||||
number. If the episode cannot be found, this will return a `404`.
|
||||
|
@ -85,3 +119,10 @@ This must be given a query paramater `q` containing the text to search for. Not
|
|||
including this will return a `406` reply. This will search the list of episode
|
||||
records for any episodes whose names match the given search terms. This is
|
||||
case-insensitive. If no episodes can be found, this will return a `404` reply.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Contributions will be judged by their technical merit. No politics on project forums.
|
||||
|
||||
All code is licensed under the MIT license.
|
||||
|
|
|
@ -9,15 +9,21 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
endpoint = "http://ponyapi.apps.xeserv.us"
|
||||
endpoint = "https://ponyapi.apps.xeserv.us"
|
||||
)
|
||||
|
||||
func getJson(fragment string) (data []byte, err error) {
|
||||
resp, err := http.Get(endpoint + fragment)
|
||||
c := &http.Client{}
|
||||
|
||||
req, err := http.NewRequest("GET", endpoint+fragment, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//req.Header.Add("X-API-Options", "bare")
|
||||
|
||||
resp, err := c.Do(req)
|
||||
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -61,6 +67,12 @@ func Newest() (*Episode, error) {
|
|||
return getEpisode("/newest")
|
||||
}
|
||||
|
||||
// LastAired returns information on the most recently aried episode
|
||||
// or an error.
|
||||
func LastAired() (*Episode, error) {
|
||||
return getEpisode("/last_aired")
|
||||
}
|
||||
|
||||
// Random returns information on a random episode.
|
||||
func Random() (*Episode, error) {
|
||||
return getEpisode("/random")
|
||||
|
|
|
@ -4,6 +4,15 @@ import "testing"
|
|||
|
||||
func TestNewestEpisode(t *testing.T) {
|
||||
ep, err := Newest()
|
||||
if err != nil {
|
||||
// t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("%#v", ep)
|
||||
}
|
||||
|
||||
func TestLastAiredEpisode(t *testing.T) {
|
||||
ep, err := LastAired()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
go test -v .
|
||||
go test .
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
.gradle
|
|
@ -0,0 +1,62 @@
|
|||
PonyAPI Java Client
|
||||
===================
|
||||
|
||||
A Java client for accessing PonyAPI. Requires Java 8.
|
||||
|
||||
Example Usage
|
||||
-------------
|
||||
|
||||
```java
|
||||
package us.xeserv.examples;
|
||||
|
||||
import us.xeserv.ponyapi.Episode;
|
||||
import us.xeserv.ponyapi.PonyApiClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
public class PonyApiExample {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
// Initialize a client with a custom API host
|
||||
PonyApiClient client = new PonyApiClient("some.fqdn.here"); // defaults to port 80
|
||||
client = new PonyApiClient("some.fqdn.here", 8080); // with a custom port number
|
||||
// Initialize a client using http://ponyapi.apps.xeserv.us/ as the API host
|
||||
client = new PonyApiClient();
|
||||
|
||||
// Get a list of all the episodes
|
||||
List<Episode> allEpisodes = client.all();
|
||||
|
||||
// Get the newest episode
|
||||
Episode newestEpisode = client.newest();
|
||||
|
||||
// Get information about an episode
|
||||
String name = newestEpisode.name;
|
||||
Instant airDate = newestEpisode.airDate;
|
||||
int seasonNumber = newestEpisode.season;
|
||||
int episodeNumber = newestEpisode.episode;
|
||||
boolean isMovie = newestEpisode.isMovie;
|
||||
|
||||
// Get all episodes in a season
|
||||
List<Episode> season = client.getSeason(1);
|
||||
|
||||
// Get all movies
|
||||
List<Episode> movies = client.getMovies();
|
||||
|
||||
// Get a specific episode by season and episode number
|
||||
Episode specificEpisode = client.getEpisode(1, 13);
|
||||
|
||||
// Get a specific movie by movie number
|
||||
Episode specificMovie = client.getMovie(1);
|
||||
|
||||
// Get a random movie or movie number
|
||||
Episode random = client.random();
|
||||
|
||||
// Get a list of all episodes and movies matching a query
|
||||
List<Episode> queryResults = client.search("Owl's Well"); // returns Owl's Well That Ends Well
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
|
@ -0,0 +1,19 @@
|
|||
group 'us.xeserv'
|
||||
version '1.0'
|
||||
|
||||
apply plugin: 'java'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "com.google.code.gson:gson:+"
|
||||
compile "org.apache.httpcomponents:httpclient:+"
|
||||
compile "org.apache.httpcomponents:fluent-hc:+"
|
||||
compile "com.google.guava:guava:+"
|
||||
testCompile "junit:junit:+"
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,6 @@
|
|||
#Tue Aug 18 15:13:15 CDT 2015
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-bin.zip
|
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
|
@ -0,0 +1,84 @@
|
|||
package us.xeserv.ponyapi;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The {@code Episode} class represents My Little Pony episode or movie information returned by PonyAPI.
|
||||
* <p>
|
||||
* Episodes are immutable; they represent data returned from the API and cannot be changed after they are created.
|
||||
* <p>
|
||||
* Movie data stored in an {@code Episode} object are referred to by the season number 99. Their episode number refers
|
||||
* to the order of their release starting with 1.
|
||||
*
|
||||
* @author Justin Kaufman
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Episode {
|
||||
|
||||
/**
|
||||
* The name of the episode or movie.
|
||||
*/
|
||||
public final String name;
|
||||
|
||||
/**
|
||||
* The season of the episode, or 99 if it is a movie.
|
||||
*/
|
||||
public final int season;
|
||||
|
||||
/**
|
||||
* The episode or movie number.
|
||||
*/
|
||||
public final int episode;
|
||||
|
||||
/**
|
||||
* The air date.
|
||||
*/
|
||||
public final Instant airDate;
|
||||
|
||||
/**
|
||||
* True if this episode represents a movie, false otherwise.
|
||||
*/
|
||||
public final boolean isMovie;
|
||||
|
||||
protected Episode(String name, int season, int episode, long airDate, boolean isMovie) {
|
||||
this.name = name;
|
||||
this.season = season;
|
||||
this.episode = episode;
|
||||
this.airDate = Instant.ofEpochSecond(airDate);
|
||||
this.isMovie = isMovie;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Episode rhs = (Episode) obj;
|
||||
return Objects.equals(name, rhs.name)
|
||||
&& Objects.equals(season, rhs.season)
|
||||
&& Objects.equals(episode, rhs.episode)
|
||||
&& Objects.equals(airDate, rhs.airDate)
|
||||
&& Objects.equals(isMovie, rhs.isMovie);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, season, episode, airDate, isMovie);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{\n"
|
||||
+ "\t\"name\": \"" + name + "\",\n"
|
||||
+ "\t\"air_date\": " + airDate.getEpochSecond() + ",\n"
|
||||
+ "\t\"season\": " + season + ",\n"
|
||||
+ "\t\"episode\": " + episode + ",\n"
|
||||
+ "\t\"is_movie\": " + isMovie + "\n"
|
||||
+ "}";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package us.xeserv.ponyapi;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gson.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class JsonDecoder {
|
||||
|
||||
private static final Gson gson;
|
||||
static {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
gsonBuilder.registerTypeAdapter(Episode.class, (JsonDeserializer<Episode>) (json, typeOfT, context) -> {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
return new Episode(
|
||||
jsonObject.get("name").getAsString(),
|
||||
jsonObject.get("season").getAsInt(),
|
||||
jsonObject.get("episode").getAsInt(),
|
||||
jsonObject.get("air_date").getAsLong(),
|
||||
jsonObject.get("is_movie").getAsBoolean()
|
||||
);
|
||||
});
|
||||
gson = gsonBuilder.create();
|
||||
}
|
||||
|
||||
protected static Episode fromJson(String json) {
|
||||
if (Strings.isNullOrEmpty(json)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JsonObject wrapper = (JsonObject) new JsonParser().parse(json);
|
||||
JsonObject payload = wrapper.get("episode").getAsJsonObject();
|
||||
return gson.fromJson(payload, Episode.class);
|
||||
} catch (Exception ignored) {
|
||||
// TODO: Logging for parse errors or passing a general parse exception
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static List<Episode> listFromJson(String json) {
|
||||
if (Strings.isNullOrEmpty(json)) {
|
||||
return null;
|
||||
}
|
||||
List<Episode> list = new ArrayList<>();
|
||||
try {
|
||||
JsonObject wrapper = (JsonObject) new JsonParser().parse(json);
|
||||
JsonArray payload = wrapper.get("episodes").getAsJsonArray();
|
||||
for (JsonElement episode : payload) {
|
||||
list.add(gson.fromJson(episode, Episode.class));
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// TODO: Logging for parse errors or passing a general parse exception
|
||||
}
|
||||
if (list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package us.xeserv.ponyapi;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.fluent.Request;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The {@code PonyApiClient} class provides an interface between Java code that uses this library and PonyAPI.
|
||||
* <p>
|
||||
* This client is able to retrieve information about the show My Little Pony: Friendship is Magic.
|
||||
*
|
||||
* @author Justin Kaufman
|
||||
* @since 1.0
|
||||
*/
|
||||
public class PonyApiClient {
|
||||
|
||||
private final String host;
|
||||
|
||||
/**
|
||||
* The default constructor initializes a client instance that uses http://ponyapi.apps.xeserv.us/ as the API source.
|
||||
*/
|
||||
public PonyApiClient() {
|
||||
this("ponyapi.apps.xeserv.us");
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a FQDN and port running the PonyAPI service on a specified port.
|
||||
* @param host a FQDN running the PonyAPI service
|
||||
* @param port the port the PonyAPI service is running on
|
||||
*/
|
||||
public PonyApiClient(String host, int port) {
|
||||
this.host = host + ":" + port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a FQDN running the PonyAPI service on port 80.
|
||||
* @param host a FQDN running the PonyAPI service
|
||||
*/
|
||||
public PonyApiClient(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all episodes and movies.
|
||||
* @return a list of all episodes and movies
|
||||
* @throws IOException if there is an error accessing the service
|
||||
*/
|
||||
public List<Episode> all() throws IOException {
|
||||
return JsonDecoder.listFromJson(asJson(get("/all")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the newest episode or movie aired.
|
||||
* @return the newest episode or movie aired
|
||||
* @throws IOException if there is an error accessing the service
|
||||
*/
|
||||
public Episode newest() throws IOException {
|
||||
return JsonDecoder.fromJson(asJson(get("/newest")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the episode or movie that most recently aired.
|
||||
* @return the episode or movie that most recently aired
|
||||
* @throws IOException if there is an error accessing the service
|
||||
*/
|
||||
public Episode lastAired() throws IOException {
|
||||
return JsonDecoder.fromJson(asJson(get("/last_aired")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all information about episodes in a given season number.
|
||||
* @param season the season number
|
||||
* @return all information about episodes in a given season number
|
||||
* @throws IOException if there is an error accessing the service
|
||||
*/
|
||||
public List<Episode> getSeason(int season) throws IOException {
|
||||
HttpResponse response = get("/season/" + season);
|
||||
if (statusCode(response) == 404) {
|
||||
return null;
|
||||
}
|
||||
return JsonDecoder.listFromJson(asJson(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all information about movies.
|
||||
* @return all information about movies
|
||||
* @throws IOException if there is an error accessing the service
|
||||
*/
|
||||
public List<Episode> getMovies() throws IOException {
|
||||
return JsonDecoder.listFromJson(asJson(get("/season/99")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all information about the episode with the given season and episode number.
|
||||
* @param season the season number
|
||||
* @param episode the episode number
|
||||
* @return all information about the episode with the given season and episode number
|
||||
* @throws IOException if there is an error accessing the service
|
||||
*/
|
||||
public Episode getEpisode(int season, int episode) throws IOException {
|
||||
HttpResponse response = get("/season/" + season + "/episode/" + episode);
|
||||
if (statusCode(response) == 404) {
|
||||
return null;
|
||||
}
|
||||
return JsonDecoder.fromJson(asJson(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all information about the movie with the given movie number.
|
||||
* @param movie the movie number
|
||||
* @return all information about the movie with the given movie number
|
||||
* @throws IOException if there is an error accessing the service
|
||||
*/
|
||||
public Episode getMovie(int movie) throws IOException {
|
||||
HttpResponse response = get("/season/99/episode/" + movie);
|
||||
if (statusCode(response) == 404) {
|
||||
return null;
|
||||
}
|
||||
return JsonDecoder.fromJson(asJson(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random episode or movie from the record.
|
||||
* @return a random episode or movie from the record
|
||||
* @throws IOException if there is an error accessing the service
|
||||
*/
|
||||
public Episode random() throws IOException {
|
||||
String json = asJson(get("/random"));
|
||||
return JsonDecoder.fromJson(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of episodes which contain the query in its name.
|
||||
* @param query a case-independent, non-empty {@code String}
|
||||
* @return a list of episodes which contain the query in its name
|
||||
* @throws IOException if there is an error accessing the service
|
||||
* @throws IllegalArgumentException if the query is null or empty
|
||||
*/
|
||||
public List<Episode> search(String query) throws IOException {
|
||||
if (Strings.isNullOrEmpty(query)) {
|
||||
throw new IllegalArgumentException("A search query is required.");
|
||||
}
|
||||
HttpResponse response = get("/search?q=" + URLEncoder.encode(query, "UTF-8"));
|
||||
if (statusCode(response) == 404) {
|
||||
return null;
|
||||
}
|
||||
return JsonDecoder.listFromJson(asJson(response));
|
||||
}
|
||||
|
||||
private HttpResponse get(String path) throws IOException {
|
||||
return Request.Get("https://" + host + path)
|
||||
.userAgent("PonyApi Java Client 1.0")
|
||||
.execute()
|
||||
.returnResponse();
|
||||
}
|
||||
|
||||
private String asJson(HttpResponse response) throws IOException {
|
||||
return EntityUtils.toString(response.getEntity());
|
||||
}
|
||||
|
||||
private int statusCode(HttpResponse response) throws IOException {
|
||||
return response.getStatusLine().getStatusCode();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package us.xeserv.ponyapi;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class JsonDecoderTest {
|
||||
|
||||
@Test
|
||||
public void malformedInputTest() {
|
||||
assertNull(JsonDecoder.fromJson(null));
|
||||
assertNull(JsonDecoder.fromJson(""));
|
||||
assertNull(JsonDecoder.fromJson("{ \"error\": { \"msg\": \"some error\", \"code\": 404 }"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void episodeTest() {
|
||||
String json = "{\n"
|
||||
+ "\t\"episode\":\n"
|
||||
+ "\t{\n"
|
||||
+ "\t\t\"name\": \"Friendship is Magic Part 1\",\n"
|
||||
+ "\t\t\"air_date\": 1286735400,\n"
|
||||
+ "\t\t\"season\": 1,\n"
|
||||
+ "\t\t\"episode\": 1,\n"
|
||||
+ "\t\t\"is_movie\": false\n"
|
||||
+ "\t}\n"
|
||||
+ "}";
|
||||
|
||||
Episode episode = JsonDecoder.fromJson(json);
|
||||
assertEquals("Friendship is Magic Part 1", episode.name);
|
||||
assertEquals(Instant.ofEpochSecond(1286735400), episode.airDate);
|
||||
assertEquals(1, episode.season);
|
||||
assertEquals(1, episode.episode);
|
||||
assertEquals(false, episode.isMovie);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void movieTest() {
|
||||
String json = "{\n"
|
||||
+ "\t\"episode\":\n"
|
||||
+ "\t{\n"
|
||||
+ "\t\t\"name\": \"Equestria Girls\",\n"
|
||||
+ "\t\t\"air_date\": 1371340800,\n"
|
||||
+ "\t\t\"season\": 99,\n"
|
||||
+ "\t\t\"episode\": 1,\n"
|
||||
+ "\t\t\"is_movie\": true\n"
|
||||
+ "\t}\n"
|
||||
+ "}";
|
||||
|
||||
Episode episode = JsonDecoder.fromJson(json);
|
||||
assertEquals("Equestria Girls", episode.name);
|
||||
assertEquals(Instant.ofEpochSecond(1371340800), episode.airDate);
|
||||
assertEquals(99, episode.season);
|
||||
assertEquals(1, episode.episode);
|
||||
assertEquals(true, episode.isMovie);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleMalformedInputTest() {
|
||||
assertNull(JsonDecoder.listFromJson(null));
|
||||
assertNull(JsonDecoder.listFromJson(""));
|
||||
assertNull(JsonDecoder.listFromJson("{ \"error\": { \"msg\": \"some error\", \"code\": 404 }"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleTest() {
|
||||
String json = "{\n"
|
||||
+ "\t\"episodes\": [\n"
|
||||
+ "\t\t{\n"
|
||||
+ "\t\t\t\"name\": \"Friendship is Magic Part 1\",\n"
|
||||
+ "\t\t\t\"air_date\": 1286735400,\n"
|
||||
+ "\t\t\t\"season\": 1,\n"
|
||||
+ "\t\t\t\"episode\": 1,\n"
|
||||
+ "\t\t\t\"is_movie\": false\n"
|
||||
+ "\t\t},\n"
|
||||
+ "\t\t{\n"
|
||||
+ "\t\t\t\"name\": \"Equestria Girls\",\n"
|
||||
+ "\t\t\t\"air_date\": 1371340800,\n"
|
||||
+ "\t\t\t\"season\": 99,\n"
|
||||
+ "\t\t\t\"episode\": 1,\n"
|
||||
+ "\t\t\t\"is_movie\": true\n"
|
||||
+ "\t\t}\n"
|
||||
+ "\t]\n"
|
||||
+ "}";
|
||||
|
||||
List<Episode> episodes = JsonDecoder.listFromJson(json);
|
||||
assertNotNull(episodes);
|
||||
assertEquals(2, episodes.size());
|
||||
|
||||
Episode episode1 = episodes.get(0);
|
||||
assertEquals("Friendship is Magic Part 1", episode1.name);
|
||||
assertEquals(Instant.ofEpochSecond(1286735400), episode1.airDate);
|
||||
assertEquals(1, episode1.season);
|
||||
assertEquals(1, episode1.episode);
|
||||
assertEquals(false, episode1.isMovie);
|
||||
|
||||
Episode episode2 = episodes.get(1);
|
||||
assertEquals("Equestria Girls", episode2.name);
|
||||
assertEquals(Instant.ofEpochSecond(1371340800), episode2.airDate);
|
||||
assertEquals(99, episode2.season);
|
||||
assertEquals(1, episode2.episode);
|
||||
assertEquals(true, episode2.isMovie);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package us.xeserv.ponyapi;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class PonyApiClientTest {
|
||||
|
||||
private PonyApiClient client = new PonyApiClient();
|
||||
|
||||
@Test
|
||||
public void allTest() throws Exception {
|
||||
List<Episode> list = client.all();
|
||||
assertFalse(list.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newestTest() throws IOException {
|
||||
Episode episode = client.newest();
|
||||
assertNotNull(episode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lastAiredTest() throws IOException {
|
||||
Episode episode = client.lastAired();
|
||||
assertNotNull(episode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSeasonTest() throws IOException {
|
||||
List<Episode> list = client.getSeason(98);
|
||||
assertNull(list);
|
||||
list = client.getSeason(1);
|
||||
assertEquals(26, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMoviesTest() throws IOException {
|
||||
List<Episode> list = client.getMovies();
|
||||
assertFalse(list.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEpisodeTest() throws IOException {
|
||||
Episode episode = client.getEpisode(98, 1);
|
||||
assertNull(episode);
|
||||
episode = client.getEpisode(1, 1);
|
||||
assertEquals("Friendship is Magic Part 1", episode.name);
|
||||
assertEquals(Instant.ofEpochSecond(1286735400), episode.airDate);
|
||||
assertEquals(1, episode.season);
|
||||
assertEquals(1, episode.episode);
|
||||
assertFalse(episode.isMovie);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMovieTest() throws IOException {
|
||||
Episode movie = client.getMovie(98);
|
||||
assertNull(movie);
|
||||
movie = client.getMovie(1);
|
||||
assertEquals("Equestria Girls", movie.name);
|
||||
assertEquals(Instant.ofEpochSecond(1371340800), movie.airDate);
|
||||
assertEquals(99, movie.season);
|
||||
assertEquals(1, movie.episode);
|
||||
assertTrue(movie.isMovie);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomTest() throws IOException {
|
||||
Episode episode = client.random();
|
||||
assertNotNull(episode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchTest() throws IOException {
|
||||
List<Episode> episodes = client.search("No Results");
|
||||
assertNull(episodes);
|
||||
episodes = client.search("Owl's Well");
|
||||
assertEquals(1, episodes.size());
|
||||
Episode episode = episodes.get(0);
|
||||
assertEquals("Owl's Well That Ends Well", episode.name);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
./gradlew test
|
|
@ -14,7 +14,7 @@ type
|
|||
is_movie*: bool ## does this record represent a movie?
|
||||
|
||||
const
|
||||
API_ENDPOINT: string = "http://ponyapi.apps.xeserv.us"
|
||||
API_ENDPOINT: string = "https://ponyapi.apps.xeserv.us"
|
||||
|
||||
proc getJson(endpoint: string): json.JsonNode =
|
||||
## get the HTTP body for the API base endpoint catted with the specific endpoint
|
||||
|
@ -44,6 +44,10 @@ proc newest*(): Episode =
|
|||
## returns information on the newest episode of My Little Pony: Friendship is Magic.
|
||||
getJson("/newest")["episode"].newEpisodeFromNode
|
||||
|
||||
proc last_aired*(): Episode =
|
||||
## returns information on the most recently aired episode of My Little Pony: Friendship is Magic.
|
||||
getJson("/last_aired")["episode"].newEpisodeFromNode
|
||||
|
||||
proc random*(): Episode =
|
||||
## returns information on a random episode.
|
||||
getJson("/random")["episode"].newEpisodeFromNode
|
||||
|
@ -72,10 +76,18 @@ when isMainModule:
|
|||
suite "ponyapi tests":
|
||||
var ep: Episode
|
||||
|
||||
test "Newest episode lookup":
|
||||
test "newest episode lookup":
|
||||
try:
|
||||
ep = newest()
|
||||
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
#fail
|
||||
|
||||
test "last aired episode lookup":
|
||||
try:
|
||||
ep = last_aired()
|
||||
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
fail
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
nim c -r ponyapi
|
||||
nim c -d:ssl --hints:off -r ponyapi
|
||||
|
|
|
@ -28,7 +28,7 @@ Available methods:
|
|||
search(query) -> return all episodes that have query in the title
|
||||
"""
|
||||
|
||||
API_ENDPOINT = "http://ponyapi.apps.xeserv.us"
|
||||
API_ENDPOINT = "https://ponyapi.apps.xeserv.us"
|
||||
|
||||
# _base_get :: Text -> Maybe [Text] -> (Maybe [Text] -> IO (Either Episode [Episode]))
|
||||
# _base_get takes a text, a splatted list of texts and returns a function such that
|
||||
|
@ -66,6 +66,9 @@ all_episodes = _base_get("/all")
|
|||
# newest :: IO Episode
|
||||
newest = _base_get("/newest")
|
||||
|
||||
# last_aired :: IO Episode
|
||||
last_aired = _base_get("/last_aired")
|
||||
|
||||
# random :: IO Episode
|
||||
random = _base_get("/random")
|
||||
|
||||
|
@ -85,9 +88,9 @@ def search(query):
|
|||
|
||||
return r.json()["episodes"]
|
||||
|
||||
# last_aired :: IO Episode
|
||||
# last_aired_old :: IO Episode
|
||||
# TODO: Does not know how to wrap around seasons, fix this
|
||||
def last_aired():
|
||||
def last_aired_old():
|
||||
new = newest()
|
||||
|
||||
if new[u"air_date"] > int(time.time()):
|
||||
|
|
|
@ -3,7 +3,10 @@ import unittest
|
|||
|
||||
class TestPonyAPI(unittest.TestCase):
|
||||
def test_newest(self):
|
||||
ponyapi.newest()
|
||||
try:
|
||||
ponyapi.newest()
|
||||
except:
|
||||
print "probably on hiatus"
|
||||
|
||||
def test_all_episodes(self):
|
||||
assert len(ponyapi.all_episodes()) > 0
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
python ./test.py
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
for client in *
|
||||
do
|
||||
if [ -d "$client" ]
|
||||
then
|
||||
(cd "$client" && ./test.sh)
|
||||
time (cd "$client" && ./test.sh)
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import json
|
||||
|
||||
type
|
||||
Episode* = object of RootObj
|
||||
## An episode of My Little Pony: Friendship is Magic
|
||||
name*: string ## Episode name
|
||||
air_date*: int ## Air date in unix time
|
||||
season*: int ## season number of the episode
|
||||
episode*: int ## the episode number in the season
|
||||
is_movie*: bool ## does this record represent a movie?
|
||||
|
||||
proc `%`*(ep: Episode): JsonNode =
|
||||
## Convert an Episode record to a JsonNode
|
||||
%*
|
||||
{
|
||||
"name": ep.name,
|
||||
"air_date": ep.air_date,
|
||||
"season": ep.season,
|
||||
"episode": ep.episode,
|
||||
"is_movie": ep.is_movie,
|
||||
}
|
||||
|
||||
proc `%`*(eps: seq[Episode]): JsonNode =
|
||||
## Convert a sequence of episodes to a JsonNode
|
||||
result = newJArray()
|
||||
|
||||
for ep in eps:
|
||||
add result, %ep
|
||||
|
||||
proc newEpisodeFromNode*(data: json.JsonNode): Episode =
|
||||
## Convert a json node into an episode object
|
||||
Episode(name: data["name"].getStr,
|
||||
air_date: data["air_date"].getNum.int,
|
||||
season: data["season"].getNum.int,
|
||||
episode: data["episode"].getNum.int,
|
||||
is_movie: data["is_movie"].getBVal)
|
||||
|
||||
proc newEpisodeListFromNode*(data: json.JsonNode): seq[Episode] =
|
||||
## Convert a json array into a sequence of episode objects
|
||||
var ret: seq[Episode]
|
||||
for item in data.items():
|
||||
ret = ret & item.newEpisodeFromNode
|
||||
|
||||
return ret
|
19
fim.list
19
fim.list
|
@ -104,6 +104,23 @@ FIM 1436023800 5 12 Amending Fences
|
|||
FIM 1436628600 5 13 Do Princesses Dream of Magic Sheep?
|
||||
FIM 1442071800 5 14 Canterlot Boutique
|
||||
FIM 1442676600 5 15 Rarity Investigates!
|
||||
FIM 1443281400 5 16 Made in Manehattan
|
||||
FIM 1443886200 5 17 Brotherhooves Social
|
||||
FIM 1444491000 5 18 Crusaders of the Lost Mark
|
||||
FIM 1445095800 5 19 The One Where Pinkie Pie Knows
|
||||
FIM 1445700600 5 20 Hearthbreakers
|
||||
FIM 1446305400 5 21 Scare Master
|
||||
FIM 1446913800 5 22 What About Discord
|
||||
FIM 1447518600 5 23 The Hooffields and McColts
|
||||
FIM 1448123400 5 24 The Mane Attraction
|
||||
FIM 1448726400 5 25 The Cutie Re-Mark Part 1
|
||||
FIM 1448728200 5 26 The Cutie Re-Mark Part 2
|
||||
FIM 1459004400 6 1 The Crystalling Part 1
|
||||
FIM 1459006200 6 2 The Crystalling Part 2
|
||||
FIM 1459611000 6 3 The Gift of the Maud Pie
|
||||
FIM 1460215800 6 4 On Your Marks
|
||||
FIM 1460820600 6 5 Gauntlet of Fire
|
||||
FIM 1462030200 6 6 No Second Prances
|
||||
FIM 1371340800 99 1 Equestria Girls
|
||||
FIM 1411862400 99 2 Equestria Girls - Rainbow Rocks
|
||||
FIM 1443277800 99 3 Equestria Girls - Friendship Games
|
||||
FIM 1443321000 99 3 Equestria Girls - Friendship Games
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
import asyncdispatch
|
||||
import episode
|
||||
import future
|
||||
import jester
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import stats
|
||||
import strutils
|
||||
import times
|
||||
|
||||
var
|
||||
episodes: seq[Episode]
|
||||
|
||||
for line in lines "./fim.list":
|
||||
var
|
||||
ep: Episode
|
||||
splitLine = line.split " "
|
||||
timestr = splitLine[1]
|
||||
seasonstr = splitLine[2]
|
||||
episodestr = splitLine[3]
|
||||
is_movie = seasonstr == "99"
|
||||
name = splitLine[4 .. ^1].join " "
|
||||
|
||||
ep = Episode(name: name,
|
||||
air_date: timestr.parseInt,
|
||||
season: seasonstr.parseInt,
|
||||
episode: episodestr.parseInt,
|
||||
is_movie: is_movie)
|
||||
|
||||
episodes = episodes & ep
|
||||
|
||||
proc `%%`(ep: Episode): JsonNode =
|
||||
## Pack an episode for PonyAPI clients.
|
||||
%*
|
||||
{
|
||||
"episode": ep,
|
||||
}
|
||||
|
||||
proc `%%`(eps: seq[Episode]): JsonNode =
|
||||
## Pack a sequence of episodes to a JsonNode for PonyAPI clients.
|
||||
%*
|
||||
{
|
||||
"episodes": eps,
|
||||
}
|
||||
|
||||
proc `%%`(why: string): JsonNode =
|
||||
## Make an error object
|
||||
%*
|
||||
{
|
||||
"error": why
|
||||
}
|
||||
|
||||
proc `%`(why: string): JsonNode =
|
||||
%%why
|
||||
|
||||
template httpReply(code, body: expr): expr =
|
||||
## Make things a lot simpler for replies, etc.
|
||||
if request.headers.getOrDefault("X-API-Options") == "bare" or @"options" == "bare":
|
||||
# New "bare" reply format, easier to scrape, etc.
|
||||
resp code, myHeaders, pretty(%body, 4)
|
||||
else:
|
||||
resp code, myHeaders, pretty(%%body, 4)
|
||||
|
||||
let myHeaders = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Powered-By": "Nim and Jester",
|
||||
"X-Git-Hash": getEnv("GIT_REV"),
|
||||
"X-Server-Epoch": $ getTime().toSeconds().int,
|
||||
}
|
||||
|
||||
settings:
|
||||
port = 5000.Port
|
||||
bindAddr = "0.0.0.0"
|
||||
|
||||
routes:
|
||||
get "/":
|
||||
"http://github.com/Xe/PonyAPI".uri.redirect
|
||||
|
||||
get "/all":
|
||||
stats.all.success.inc
|
||||
httpReply Http200, episodes
|
||||
|
||||
get "/newest":
|
||||
var
|
||||
now = getTime()
|
||||
ep: Episode
|
||||
|
||||
for episode in episodes:
|
||||
var then = times.fromSeconds(episode.air_date)
|
||||
|
||||
if now < then:
|
||||
ep = episode
|
||||
break
|
||||
|
||||
if ep.season == 0:
|
||||
stats.newest.fails.inc
|
||||
halt Http404, "No new episode found, hiatus?"
|
||||
|
||||
stats.newest.success.inc
|
||||
httpReply Http200, ep
|
||||
|
||||
get "/random":
|
||||
stats.newest.success.inc
|
||||
httpReply Http200, episodes.randomChoice()
|
||||
|
||||
get "/last_aired":
|
||||
var
|
||||
#now = getTime()
|
||||
ep: Episode
|
||||
|
||||
for epid, episode in pairs[Episode](episodes):
|
||||
# XXX HACK PLEASE FIX
|
||||
if episode.season == 5 and episode.episode == 26:
|
||||
ep = episode
|
||||
|
||||
stats.lastAired.success.inc
|
||||
httpReply Http200, ep
|
||||
|
||||
get "/season/@snumber":
|
||||
var
|
||||
season: int = @"snumber".parseInt
|
||||
eps: seq[Episode] = lc[x | (x <- episodes, x.season == season), Episode]
|
||||
|
||||
if eps.len == 0:
|
||||
stats.seasonLookup.fails.inc
|
||||
httpReply Http404, "No episodes found"
|
||||
else:
|
||||
stats.seasonLookup.success.inc
|
||||
httpReply Http200, eps
|
||||
|
||||
get "/season/@snumber/episode/@epnumber":
|
||||
var
|
||||
season: int = @"snumber".parseInt
|
||||
enumber: int = @"epnumber".parseInt
|
||||
ep: Episode
|
||||
|
||||
for episode in episodes:
|
||||
if episode.season == season:
|
||||
if episode.episode == enumber:
|
||||
ep = episode
|
||||
|
||||
if @"format" == "irccmd":
|
||||
let
|
||||
irccmd = "/cs episode del $1 $2\n/cs episode add $1 $2 $3 $4" % [$ep.season, $ep.episode, $ep.air_date, ep.name]
|
||||
echo irccmd
|
||||
|
||||
if ep.air_date == 0:
|
||||
stats.episodeLookup.fails.inc
|
||||
httpReply Http404, "Not found"
|
||||
else:
|
||||
stats.episodeLookup.success.inc
|
||||
httpReply Http200, ep
|
||||
|
||||
get "/search":
|
||||
var
|
||||
query = @"q".toLower
|
||||
|
||||
if query == "":
|
||||
stats.search.fails.inc
|
||||
halt Http406, myHeaders, pretty(%%"Need to specify a query", 4)
|
||||
|
||||
var
|
||||
eps: seq[Episode] =
|
||||
lc[x | (x <- episodes, x.name.toLower.contains query), Episode]
|
||||
|
||||
if eps.len == 0:
|
||||
stats.search.fails.inc
|
||||
httpReply Http404, "No episodes found"
|
||||
else:
|
||||
stats.search.success.inc
|
||||
httpReply Http200, eps
|
||||
|
||||
get "/_stats":
|
||||
resp Http200, myHeaders, pretty(%*
|
||||
[
|
||||
stats.all,
|
||||
stats.newest,
|
||||
stats.random,
|
||||
stats.lastAired,
|
||||
stats.seasonLookup,
|
||||
stats.episodeLookup,
|
||||
stats.search,
|
||||
], 4)
|
||||
|
||||
when isMainModule:
|
||||
runForever()
|
||||
else:
|
||||
quit "This should not be called outside of being the main module"
|
|
@ -1,10 +1,11 @@
|
|||
[Package]
|
||||
name = "ponyapi"
|
||||
version = "0.1.0"
|
||||
author = "Christine Dodrill <xena@yolo-swag.com>"
|
||||
description = "PonyAPI client https://github.com/Xe/PonyAPI"
|
||||
license = "MIT"
|
||||
srcDir = "client/nim"
|
||||
# Package
|
||||
|
||||
[Deps]
|
||||
Requires: "nim >= 0.10.0"
|
||||
version = "0.2.0"
|
||||
author = "Cadey Dodrill"
|
||||
description = "PonyAPI server https://github.com/Xe/PonyAPI"
|
||||
license = "MIT"
|
||||
bin = @["ponyapi"]
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 0.13.0", "jester#head", "random#head", "sam#head", "jsmn#head"
|
||||
|
|
105
ponyapi.py
105
ponyapi.py
|
@ -1,105 +0,0 @@
|
|||
import datetime
|
||||
import os
|
||||
import random
|
||||
|
||||
from flask import Flask, abort, jsonify, request, redirect
|
||||
|
||||
# An Episode is constructed as such:
|
||||
# data Episode = Episode
|
||||
# { name :: String
|
||||
# , air_date :: Int
|
||||
# , season :: Int
|
||||
# , episode :: Int
|
||||
# , is_movie :: Bool
|
||||
# }
|
||||
#
|
||||
# Any instance of season 99 should be interpreted as a movie.
|
||||
episodes = []
|
||||
|
||||
# First, open the input file and read in episodes
|
||||
with open("./fim.list", "r") as f:
|
||||
for line in f:
|
||||
airdate, s, e, name = line.split(' ', 4)[1:]
|
||||
|
||||
episode = {
|
||||
"name": name[:-1],
|
||||
"air_date": int(airdate),
|
||||
"season": int(s),
|
||||
"episode": int(e),
|
||||
"is_movie": s == "99"
|
||||
}
|
||||
|
||||
episodes.append(episode)
|
||||
|
||||
# Now, initialize Flask
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/")
|
||||
def hello():
|
||||
return redirect("https://github.com/Xe/PonyAPI#ponyapi", code=302)
|
||||
|
||||
@app.route("/all")
|
||||
def all_episodes():
|
||||
return jsonify(episodes=episodes)
|
||||
|
||||
@app.route("/newest")
|
||||
def newest_episode():
|
||||
now = datetime.datetime(2006, 1, 4)
|
||||
now = now.now()
|
||||
|
||||
for episode in episodes:
|
||||
if now.fromtimestamp(episode["air_date"]) > now:
|
||||
return jsonify(episode=episode)
|
||||
|
||||
abort(500)
|
||||
|
||||
@app.route("/season/<number>")
|
||||
def season(number):
|
||||
retEpisodes = []
|
||||
|
||||
for episode in episodes:
|
||||
if str(episode["season"]) == number:
|
||||
retEpisodes.append(episode)
|
||||
|
||||
try:
|
||||
assert len(retEpisodes) > 0
|
||||
except:
|
||||
abort(404)
|
||||
|
||||
return jsonify(episodes=retEpisodes)
|
||||
|
||||
@app.route("/season/<snumber>/episode/<epnumber>")
|
||||
def show_episode(snumber, epnumber):
|
||||
for episode in episodes:
|
||||
if str(episode["season"]) == snumber and str(episode["episode"]) == epnumber:
|
||||
return jsonify(episode=episode)
|
||||
|
||||
abort(404)
|
||||
|
||||
@app.route("/random")
|
||||
def show_random_ep():
|
||||
return jsonify(episode=random.choice(episodes))
|
||||
|
||||
@app.route("/search")
|
||||
def search():
|
||||
retEpisodes = []
|
||||
term = request.args.get("q", "").lower()
|
||||
|
||||
try:
|
||||
assert term != ""
|
||||
except:
|
||||
abort(406)
|
||||
|
||||
for episode in episodes:
|
||||
if term in episode["name"].lower():
|
||||
retEpisodes.append(episode)
|
||||
|
||||
try:
|
||||
assert len(retEpisodes) > 0
|
||||
except:
|
||||
abort(404)
|
||||
|
||||
return jsonify(episodes=retEpisodes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host="0.0.0.0", debug=True)
|
|
@ -489,7 +489,7 @@ pre {
|
|||
box-sizing: border-box;
|
||||
min-width: calc(100% - 19.5px);
|
||||
padding: 9.5px;
|
||||
margin: 0 10px 0px 10px;
|
||||
margin: 0.25em 10px 0.25em 10px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
white-space: pre !important;
|
||||
|
@ -892,6 +892,10 @@ div.align-right {
|
|||
|
||||
/* div.align-center * { */
|
||||
/* text-align: left } */
|
||||
|
||||
ul.simple > li {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
ol.simple, ul.simple {
|
||||
margin-bottom: 1em; }
|
||||
|
||||
|
@ -1160,11 +1164,11 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator
|
|||
<ul class="simple simple-toc-section">
|
||||
<li><a class="reference" href="#Episode"
|
||||
title="Episode = object of RootObj
|
||||
name*: string ## Episode name
|
||||
air_date*: int ## Air date in unix time
|
||||
season*: int ## season number of the episode
|
||||
episode*: int ## the episode number in the season
|
||||
is_movie*: bool ## does this record represent a movie?"><wbr />Episode</a></li>
|
||||
name*: string ## Episode name
|
||||
air_date*: int ## Air date in unix time
|
||||
season*: int ## season number of the episode
|
||||
episode*: int ## the episode number in the season
|
||||
is_movie*: bool ## does this record represent a movie?"><wbr />Episode</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -1173,6 +1177,8 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator
|
|||
<ul class="simple simple-toc-section">
|
||||
<li><a class="reference" href="#newest,"
|
||||
title="newest(): Episode"><wbr />newest</a></li>
|
||||
<li><a class="reference" href="#last_aired,"
|
||||
title="last_aired(): Episode"><wbr />last_<wbr />aired</a></li>
|
||||
<li><a class="reference" href="#random,"
|
||||
title="random(): Episode"><wbr />random</a></li>
|
||||
<li><a class="reference" href="#get_episode,int,int"
|
||||
|
@ -1201,11 +1207,11 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator
|
|||
<h1><a class="toc-backref" href="#7">Types</a></h1>
|
||||
<dl class="item">
|
||||
<dt id="Episode"><a name="Episode"></a><pre><span class="Identifier">Episode</span> <span class="Other">=</span> <span class="Keyword">object</span> <span class="Keyword">of</span> <span class="Identifier">RootObj</span>
|
||||
<span class="Identifier">name</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">string</span> <span class="Comment">## Episode name</span>
|
||||
<span class="Identifier">air_date</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span> <span class="Comment">## Air date in unix time</span>
|
||||
<span class="Identifier">season</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span> <span class="Comment">## season number of the episode</span>
|
||||
<span class="Identifier">episode</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span> <span class="Comment">## the episode number in the season</span>
|
||||
<span class="Identifier">is_movie</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">bool</span> <span class="Comment">## does this record represent a movie?</span>
|
||||
<span class="Identifier">name</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">string</span> <span class="Comment">## Episode name</span>
|
||||
<span class="Identifier">air_date</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span> <span class="Comment">## Air date in unix time</span>
|
||||
<span class="Identifier">season</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span> <span class="Comment">## season number of the episode</span>
|
||||
<span class="Identifier">episode</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span> <span class="Comment">## the episode number in the season</span>
|
||||
<span class="Identifier">is_movie</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">bool</span> <span class="Comment">## does this record represent a movie?</span>
|
||||
</pre></dt>
|
||||
<dd>
|
||||
An episode of My Little Pony: Friendship is Magic
|
||||
|
@ -1216,45 +1222,53 @@ An episode of My Little Pony: Friendship is Magic
|
|||
<div class="section" id="12">
|
||||
<h1><a class="toc-backref" href="#12">Procs</a></h1>
|
||||
<dl class="item">
|
||||
<dt id="newest"><a name="newest,"></a><pre><span class="Keyword">proc</span> <span class="Identifier">newest</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Episode</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">HttpRequestError</span><span class="Other">,</span>
|
||||
<span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span>
|
||||
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">JsonParsingError</span><span class="Other">]</span><span class="Other">,</span>
|
||||
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">TimeEffect</span><span class="Other">]</span><span class="Other">.}</span></pre></dt>
|
||||
<dt id="newest"><a name="newest,"></a><pre><span class="Keyword">proc</span> <span class="Identifier">newest</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Episode</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">HttpRequestError</span><span class="Other">,</span> <span class="Identifier">OverflowError</span><span class="Other">,</span>
|
||||
<span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
|
||||
<span class="Identifier">JsonParsingError</span><span class="Other">]</span><span class="Other">,</span>
|
||||
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">TimeEffect</span><span class="Other">]</span><span class="Other">.}</span></pre></dt>
|
||||
<dd>
|
||||
returns information on the newest episode of My Little Pony: Friendship is Magic.
|
||||
|
||||
</dd>
|
||||
<dt id="random"><a name="random,"></a><pre><span class="Keyword">proc</span> <span class="Identifier">random</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Episode</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">HttpRequestError</span><span class="Other">,</span>
|
||||
<span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span>
|
||||
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">JsonParsingError</span><span class="Other">]</span><span class="Other">,</span>
|
||||
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">TimeEffect</span><span class="Other">]</span><span class="Other">.}</span></pre></dt>
|
||||
<dt id="last_aired"><a name="last_aired,"></a><pre><span class="Keyword">proc</span> <span class="Identifier">last_aired</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Episode</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">HttpRequestError</span><span class="Other">,</span>
|
||||
<span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span>
|
||||
<span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">JsonParsingError</span><span class="Other">]</span><span class="Other">,</span>
|
||||
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">TimeEffect</span><span class="Other">]</span><span class="Other">.}</span></pre></dt>
|
||||
<dd>
|
||||
returns information on the most recently aired episode of My Little Pony: Friendship is Magic.
|
||||
|
||||
</dd>
|
||||
<dt id="random"><a name="random,"></a><pre><span class="Keyword">proc</span> <span class="Identifier">random</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Episode</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">HttpRequestError</span><span class="Other">,</span> <span class="Identifier">OverflowError</span><span class="Other">,</span>
|
||||
<span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
|
||||
<span class="Identifier">JsonParsingError</span><span class="Other">]</span><span class="Other">,</span>
|
||||
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">TimeEffect</span><span class="Other">]</span><span class="Other">.}</span></pre></dt>
|
||||
<dd>
|
||||
returns information on a random episode.
|
||||
|
||||
</dd>
|
||||
<dt id="get_episode"><a name="get_episode,int,int"></a><pre><span class="Keyword">proc</span> <span class="Identifier">get_episode</span><span class="Other">(</span><span class="Identifier">season</span><span class="Other">,</span> <span class="Identifier">episode</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Episode</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
|
||||
<span class="Identifier">HttpRequestError</span><span class="Other">,</span> <span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
|
||||
<dt id="get_episode"><a name="get_episode,int,int"></a><pre><span class="Keyword">proc</span> <span class="Identifier">get_episode</span><span class="Other">(</span><span class="Identifier">season</span><span class="Other">,</span> <span class="Identifier">episode</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Episode</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
|
||||
<span class="Identifier">HttpRequestError</span><span class="Other">,</span> <span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
|
||||
<span class="Identifier">JsonParsingError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">TimeEffect</span><span class="Other">]</span><span class="Other">.}</span></pre></dt>
|
||||
<dd>
|
||||
return an arbitrary episode by season, episode pair.
|
||||
|
||||
</dd>
|
||||
<dt id="all_episodes"><a name="all_episodes,"></a><pre><span class="Keyword">proc</span> <span class="Identifier">all_episodes</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">Episode</span><span class="Other">]</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
|
||||
<span class="Identifier">HttpRequestError</span><span class="Other">,</span> <span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
|
||||
<span class="Identifier">JsonParsingError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">TimeEffect</span><span class="Other">]</span><span class="Other">.}</span></pre></dt>
|
||||
<dt id="all_episodes"><a name="all_episodes,"></a><pre><span class="Keyword">proc</span> <span class="Identifier">all_episodes</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">Episode</span><span class="Other">]</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span> <span class="Identifier">HttpRequestError</span><span class="Other">,</span>
|
||||
<span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">JsonParsingError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span>
|
||||
<span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">TimeEffect</span><span class="Other">]</span><span class="Other">.}</span></pre></dt>
|
||||
<dd>
|
||||
return all information on all episodes.
|
||||
|
||||
</dd>
|
||||
<dt id="get_season"><a name="get_season,int"></a><pre><span class="Keyword">proc</span> <span class="Identifier">get_season</span><span class="Other">(</span><span class="Identifier">season</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">Episode</span><span class="Other">]</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
|
||||
<span class="Identifier">HttpRequestError</span><span class="Other">,</span> <span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
|
||||
<dt id="get_season"><a name="get_season,int"></a><pre><span class="Keyword">proc</span> <span class="Identifier">get_season</span><span class="Other">(</span><span class="Identifier">season</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">Episode</span><span class="Other">]</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
|
||||
<span class="Identifier">HttpRequestError</span><span class="Other">,</span> <span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
|
||||
<span class="Identifier">JsonParsingError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">TimeEffect</span><span class="Other">]</span><span class="Other">.}</span></pre></dt>
|
||||
<dd>
|
||||
return all information on a single season.
|
||||
|
||||
</dd>
|
||||
<dt id="search"><a name="search,string"></a><pre><span class="Keyword">proc</span> <span class="Identifier">search</span><span class="Other">(</span><span class="Identifier">term</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">Episode</span><span class="Other">]</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
|
||||
<span class="Identifier">HttpRequestError</span><span class="Other">,</span> <span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
|
||||
<dt id="search"><a name="search,string"></a><pre><span class="Keyword">proc</span> <span class="Identifier">search</span><span class="Other">(</span><span class="Identifier">term</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">Episode</span><span class="Other">]</span> <span class="Other">{.</span><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">OSError</span><span class="Other">,</span>
|
||||
<span class="Identifier">HttpRequestError</span><span class="Other">,</span> <span class="Identifier">OverflowError</span><span class="Other">,</span> <span class="Identifier">TimeoutError</span><span class="Other">,</span> <span class="Identifier">ProtocolError</span><span class="Other">,</span> <span class="Identifier">Exception</span><span class="Other">,</span>
|
||||
<span class="Identifier">JsonParsingError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ReadIOEffect</span><span class="Other">,</span> <span class="Identifier">WriteIOEffect</span><span class="Other">,</span> <span class="Identifier">TimeEffect</span><span class="Other">]</span><span class="Other">.}</span></pre></dt>
|
||||
<dd>
|
||||
searches for episodes by the given query term.
|
||||
|
@ -1270,7 +1284,7 @@ searches for episodes by the given query term.
|
|||
<div class="twelve-columns footer">
|
||||
<span class="nim-sprite"></span>
|
||||
<br>
|
||||
<small>Made with Nim. Generated: 2015-08-12 17:29:43 UTC</small>
|
||||
<small>Made with Nim. Generated: 2015-08-17 15:01:16 UTC</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,8 +0,0 @@
|
|||
Flask==0.10.1
|
||||
Jinja2==2.8
|
||||
MarkupSafe==0.23
|
||||
Werkzeug==0.10.4
|
||||
argparse==1.2.1
|
||||
gunicorn==19.3.0
|
||||
itsdangerous==0.24
|
||||
wsgiref==0.1.2
|
|
@ -0,0 +1,7 @@
|
|||
ponyapi.apps.xeserv.us {
|
||||
log syslog
|
||||
|
||||
proxy / http://127.0.0.1:6452 {
|
||||
proxy_header Host {host}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
[Unit]
|
||||
Description=PonyAPI Server listening on port 6452
|
||||
Requires=docker.service
|
||||
Restart=always
|
||||
|
||||
[Service]
|
||||
ExecStartPre=-/usr/bin/docker pull xena/ponyapi
|
||||
ExecStartPre=-/usr/bin/docker rm -f ponyapi
|
||||
ExecStart=/usr/bin/docker run -p 6452:5000 --name ponyapi xena/ponyapi
|
||||
ExecStop=/usr/bin/docker rm -f ponyapi
|
||||
|
||||
[Install]
|
||||
WantedBy=network.target
|
|
@ -0,0 +1,25 @@
|
|||
import json
|
||||
|
||||
type
|
||||
Bucket* = object of RootObj
|
||||
## A collection of stats
|
||||
name*: string
|
||||
fails*: int
|
||||
success*: int
|
||||
|
||||
proc `%`*(b: Bucket): JsonNode =
|
||||
%*
|
||||
{
|
||||
"name": b.name,
|
||||
"fails": b.fails,
|
||||
"success": b.success,
|
||||
}
|
||||
|
||||
var
|
||||
all* = Bucket(name: "all")
|
||||
newest* = Bucket(name: "newest")
|
||||
random* = Bucket(name: "random")
|
||||
lastAired* = Bucket(name: "lastAired")
|
||||
seasonLookup* = Bucket(name: "seasonLookup")
|
||||
episodeLookup* = Bucket(name: "episodeLookup")
|
||||
search* = Bucket(name: "search")
|
Loading…
Reference in New Issue