aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Armstrong <jeff@approximatrix.com>2021-04-02 13:08:37 -0400
committerJeffrey Armstrong <jeff@approximatrix.com>2021-04-02 13:08:37 -0400
commitb27bd7cfa58eb82fc2a6c76aaa848e07a6fa7c24 (patch)
treef611937418d1ea5416431715ab1f6a44d42d1905
parentda47fdfddc46e35a939b7771eda21debec50c094 (diff)
downloadlevitating-b27bd7cfa58eb82fc2a6c76aaa848e07a6fa7c24.tar.gz
levitating-b27bd7cfa58eb82fc2a6c76aaa848e07a6fa7c24.zip
API calls to the gemini interface should work. API calls to the titan interface need implementation.
-rw-r--r--captain/api.f9056
-rw-r--r--captain/db.f90129
-rw-r--r--captain/external.f9024
-rw-r--r--captain/levitating-captain.prj3
-rw-r--r--captain/response.f9045
-rw-r--r--captain/special.f9053
6 files changed, 284 insertions, 26 deletions
diff --git a/captain/api.f90 b/captain/api.f90
index 025251d..48cae8a 100644
--- a/captain/api.f90
+++ b/captain/api.f90
@@ -1,27 +1,75 @@
module api_handling
implicit none
+ character(*), parameter::RESPONSE_JSON_OKAY = '{"status": "okay"}'
+ character(*), parameter::RESPONSE_JSON_IDLE = '{"status": "idle"}'
+
contains
function api_request_gemini(req) result(resp)
use server_response
+ use captain_db
+ use special_filenames
implicit none
type(request), intent(in)::req
type(response)::resp
- integer::i
+ character(8)::job_text, task_text
+ character(PLAYER_NAME_LENGTH)::player
+ integer::job_i, task_i, player_i, instruction_i, ierr
! Complete - "/api/player/{name}/job/{jobid}/complete"
! Failed - "/api/player/{name}/job/{jobid}/failed"
! Task - "/api/player/{name}/job/{jobid}/task/{task num}"
if(trim(req%component(2)) == "player" .and. trim(req%component(4)) == "job") then
- if(trim(req%component(6)) == "complete") then
-
+ call req%path_component(5, job_text)
+ read(job_text, *, iostat=ierr) job_i
+
+ if(ierr == 0 .and. .not. is_final_job_status(job_i)) then
+ if(trim(req%component(6)) == "complete") then
+ call update_job_status(job_i, JOB_STATUS_SUCCESS)
+ else if(trim(req%component(6)) == "failure") then
+ call update_job_status(job_i, JOB_STATUS_FAILURE)
+ end if
+ end if
+
+ if(ierr == 0 .and. trim(req%component(6)) == "task") then
+ call req%path_component(7, task_text)
+ read(task_text, *, iostat=ierr) task_i
+ if(ierr == 0) then
+ if(req%query_string == "starting" .or. req%query_string == "inprogress") then
+ call update_task_status(job_i, task_i, JOB_STATUS_WORKING)
+ else if(req%query_string == "complete") then
+ call update_task_status(job_i, task_i, JOB_STATUS_SUCCESS)
+ else if(req%query_string == "failed") then
+ call update_task_status(job_i, task_i, JOB_STATUS_FAILURE)
+ end if
+ end if
+ end if
+
+ resp%code = GEMINI_CODE_SUCCESS
+ call resp%set_body_contents(RESPONSE_JSON_OKAY)
+
+ ! Checkin - /api/player/{name}/checkin.json
+ else if(trim(req%component(2)) == "player" .and. trim(req%component(4)) == "checkin.json") then
+ ! Check for pending jobs
+ call req%path_component(3, player)
+ player_i = get_player_id(player)
+
+ job_i = get_pending_job_for_player(player_i)
+ if(job_i < 0) then
+ resp%code = GEMINI_CODE_SUCCESS
+ call resp%set_body_contents(RESPONSE_JSON_IDLE)
+ else
+ resp%code = GEMINI_CODE_SUCCESS
+ instruction_i = get_job_instruction(job_i)
+ resp%body_filename => get_instructions_static_filename(instruction_i)
+ resp%temporary_file = .false.
+ resp%body_mimetype = "application/json"
end if
end if
-
end function api_request_gemini
function api_request_titan(req) result(resp)
diff --git a/captain/db.f90 b/captain/db.f90
index c23b770..fa53774 100644
--- a/captain/db.f90
+++ b/captain/db.f90
@@ -568,6 +568,135 @@ contains
end function get_job_tasks
+ subroutine update_job_status(job_id, status)
+ implicit none
+
+ integer, intent(in)::job_id, status
+ type(sqlite3_stmt)::stmt
+
+ if(stmt%prepare(db, "UPDATE jobs SET status=? WHERE id=?") == SQLITE_OK) then
+ if(stmt%bind_int(1, status) == SQLITE_OK .and. stmt%bind_int(2, job_id) == SQLITE_OK) then
+ call stmt%step_now()
+ end if
+ end if
+ call stmt%finalize()
+
+ call update_job_time(job_id)
+
+ end subroutine update_job_status
+
+ subroutine update_job_time(job_id)
+ implicit none
+
+ integer, intent(in)::job_id
+ character(8)::update_date
+ character(10)::update_time
+ type(sqlite3_stmt)::stmt
+
+ call date_and_time(date=update_date, time=update_time)
+
+ if(stmt%prepare(db, "UPDATE jobs SET time=? WHERE id=?") == SQLITE_OK) then
+ if(stmt%bind_text(1, update_date//" "//update_time) == SQLITE_OK .and. &
+ stmt%bind_int(2, job_id) == SQLITE_OK) then
+ call stmt%step_now()
+ end if
+ end if
+ call stmt%finalize()
+
+ end subroutine update_job_time
+
+ subroutine update_task_status(job_id, task_id, status)
+ implicit none
+
+ integer, intent(in)::job_id, task_id, status
+ type(sqlite3_stmt)::stmt
+
+ if(stmt%prepare(db, "UPDATE jobs SET status=? WHERE job=? AND task=?") == SQLITE_OK) then
+ if(stmt%bind_int(1, status) == SQLITE_OK .and. &
+ stmt%bind_int(2, job_id) == SQLITE_OK .and. &
+ stmt%bind_int(3, task_id) == SQLITE_OK) then
+ call stmt%step_now()
+ end if
+ end if
+ call stmt%finalize()
+
+ call update_job_time(job_id)
+
+ end subroutine update_task_status
+
+ function is_final_job_status(job_id)
+ implicit none
+
+ integer, intent(in)::job_id
+ logical::is_final_job_status
+ integer::i
+
+ i = get_job_status(job_id)
+
+ is_final_job_status = (i == JOB_STATUS_SUCCESS .or. i == JOB_STATUS_FAILURE)
+
+ end function is_final_job_status
+
+ function get_job_status(job_id) result(status)
+ implicit none
+
+ integer, intent(in)::job_id
+ integer::status
+ type(sqlite3_stmt)::stmt
+
+ status = -1
+
+ if(stmt%prepare(db, "SELECT status FROM jobs WHERE id=? LIMIT 1") == SQLITE_OK) then
+ if(stmt%bind_int(1, job_id) == SQLITE_OK) then
+ if(stmt%step() == SQLITE_ROW) then
+ status = stmt%column_int(0)
+ end if
+ end if
+ end if
+ call stmt%finalize()
+
+ end function get_job_status
+
+ function get_job_instruction(job_id) result(status)
+ implicit none
+
+ integer, intent(in)::job_id
+ integer::status
+ type(sqlite3_stmt)::stmt
+
+ status = -1
+
+ if(stmt%prepare(db, "SELECT instruction FROM jobs WHERE id=? LIMIT 1") == SQLITE_OK) then
+ if(stmt%bind_int(1, job_id) == SQLITE_OK) then
+ if(stmt%step() == SQLITE_ROW) then
+ status = stmt%column_int(0)
+ end if
+ end if
+ end if
+ call stmt%finalize()
+
+ end function get_job_instruction
+
+ function get_pending_job_for_player(player) result(job)
+ implicit none
+
+ integer, intent(in)::player
+ integer::job
+ type(sqlite3_stmt)::stmt
+
+ job = -1
+
+ if(stmt%prepare(db, "SELECT id FROM jobs WHERE player=? AND status=? LIMIT 1") == SQLITE_OK) then
+ if(stmt%bind_int(1, player) == SQLITE_OK .and. stmt%bind_int(2, JOB_STATUS_PENDING) == SQLITE_OK) then
+ if(stmt%step() == SQLITE_ROW) then
+ job = stmt%column_int(0)
+ end if
+ end if
+ end if
+ call stmt%finalize()
+
+ end function get_pending_job_for_player
+
subroutine scan_instructions_for_db()
use config
use utilities
diff --git a/captain/external.f90 b/captain/external.f90
index a86bd52..97fa69c 100644
--- a/captain/external.f90
+++ b/captain/external.f90
@@ -595,6 +595,7 @@ contains
use config
use utilities
use server_response
+ use special_filenames
implicit none
class(request), intent(in)::req
@@ -604,28 +605,7 @@ contains
logical::exists
resp%temporary_file = .false.
-
- call req%path_component(1, category)
- call req%last_component(filename)
-
- call write_log("Catgeory: "//trim(category)//" File: "//trim(filename))
-
- if(category == "releases") then
- allocate(character(len=(len_trim(release_dir)+len_trim(filename)+1)) :: resp%body_filename)
- call combine_paths(release_dir, filename, resp%body_filename)
- else if(category == "uploads") then
- allocate(character(len=(len_trim(release_dir)+len_trim(filename)+1)) :: resp%body_filename)
- call combine_paths(release_dir, filename, resp%body_filename)
- else if(category == "results") then
- allocate(character(len=(len_trim(results_dir)+len_trim(filename)+1)) :: resp%body_filename)
- call combine_paths(results_dir, filename, resp%body_filename)
- else if(category == "static") then
- allocate(character(len=(len_trim(static_dir)+len_trim(filename)+1)) :: resp%body_filename)
- call combine_paths(static_dir, filename, resp%body_filename)
- else if(category == "favicon.txt") then
- allocate(character(len=(len_trim(static_dir)+len_trim(filename)+1)) :: resp%body_filename)
- call combine_paths(static_dir, filename, resp%body_filename)
- end if
+ resp%body_filename => get_special_full_filename(category, filename)
inquire(file=resp%body_filename, exist=exists)
if(.not. exists) then
diff --git a/captain/levitating-captain.prj b/captain/levitating-captain.prj
index 415398c..6e48fb2 100644
--- a/captain/levitating-captain.prj
+++ b/captain/levitating-captain.prj
@@ -76,6 +76,9 @@
"filename":".\\response.f90",
"enabled":"1"
},{
+ "filename":".\\special.f90",
+ "enabled":"1"
+ },{
"filename":".\\sqlite.f90",
"enabled":"1"
},{
diff --git a/captain/response.f90 b/captain/response.f90
index e1d80eb..2f48caa 100644
--- a/captain/response.f90
+++ b/captain/response.f90
@@ -24,6 +24,8 @@ module server_response
procedure :: destroy => response_destroy
procedure :: set_message => response_set_message
procedure :: set_url => response_set_url
+ procedure :: set_body_contents => response_temp_file_contents
+ procedure :: set_filename => response_set_filename_using_allocation
end type
@@ -255,4 +257,47 @@ contains
end subroutine response_set_url
+ subroutine response_temp_file_contents(resp, str, mimetype)
+ use utilities, only: generate_temporary_filename
+ implicit none
+
+ class(response)::resp
+ character(*), intent(in)::str
+ character(*), intent(in), optional::mimetype
+
+ integer::unum
+
+ resp%body_filename => generate_temporary_filename()
+ open(newunit=unum, file=resp%body_filename, action="write", status="new")
+ write(unum, *) str
+ close(unum)
+
+ resp%temporary_file = .true.
+
+ if(present(mimetype)) then
+ resp%body_mimetype = mimetype
+ else
+ resp%body_mimetype = "text/plain"
+ end if
+
+ end subroutine response_temp_file_contents
+
+ subroutine response_set_filename_using_allocation(resp, fname, mimetype)
+ implicit none
+
+ class(response)::resp
+ character(*), intent(in)::fname
+ character(*), intent(in), optional::mimetype
+
+ allocate(character(len=len_trim(fname)) :: resp%body_filename)
+ resp%body_filename = trim(fname)
+
+ if(present(mimetype)) then
+ resp%body_mimetype = mimetype
+ else
+ resp%body_mimetype = "text/plain"
+ end if
+
+ end subroutine response_set_filename_using_allocation
+
end module server_response
diff --git a/captain/special.f90 b/captain/special.f90
new file mode 100644
index 0000000..1bbf220
--- /dev/null
+++ b/captain/special.f90
@@ -0,0 +1,53 @@
+module special_filenames
+implicit none
+
+contains
+
+ function get_special_full_filename(category, filename) result(res)
+ use utilities, only: combine_paths
+ use config
+ implicit none
+
+ character(*), intent(in)::category, filename
+ character(len=:), pointer::res
+
+ res => null()
+
+ if(trim(category) == "releases") then
+ allocate(character(len=(len_trim(release_dir)+len_trim(filename)+1)) :: res)
+ call combine_paths(release_dir, filename, res)
+ else if(trim(category) == "uploads") then
+ allocate(character(len=(len_trim(release_dir)+len_trim(filename)+1)) :: res)
+ call combine_paths(release_dir, filename, res)
+ else if(trim(category) == "results") then
+ allocate(character(len=(len_trim(results_dir)+len_trim(filename)+1)) :: res)
+ call combine_paths(results_dir, filename, res)
+ else if(trim(category) == "static") then
+ allocate(character(len=(len_trim(static_dir)+len_trim(filename)+1)) :: res)
+ call combine_paths(static_dir, filename, res)
+ else if(trim(category) == "favicon.txt") then
+ allocate(character(len=(len_trim(static_dir)+len_trim(filename)+1)) :: res)
+ call combine_paths(static_dir, filename, res)
+ else if(trim(category) == "instructions") then
+ allocate(character(len=(len_trim(instructions_dir)+len_trim(filename)+1)) :: res)
+ call combine_paths(instructions_dir, filename, res)
+ end if
+
+ end function get_special_full_filename
+
+ function get_instructions_static_filename(instruction_id) result(res)
+ use captain_db
+ implicit none
+
+ integer, intent(in)::instruction_id
+ character(len=:), pointer::res
+
+ character(PLAYER_NAME_LENGTH)::instruction_name
+
+ call get_instruction_name(instruction_id, instruction_name)
+
+ res => get_special_full_filename("instructions", trim(instruction_name)//".json")
+
+ end function get_instructions_static_filename
+
+end module special_filenames