diff options
Diffstat (limited to 'captain')
-rw-r--r-- | captain/api.f90 | 56 | ||||
-rw-r--r-- | captain/db.f90 | 129 | ||||
-rw-r--r-- | captain/external.f90 | 24 | ||||
-rw-r--r-- | captain/levitating-captain.prj | 3 | ||||
-rw-r--r-- | captain/response.f90 | 45 | ||||
-rw-r--r-- | captain/special.f90 | 53 |
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 |