Added item-reports endpoint, schematic-exporter2.lua
This commit is contained in:
		
							parent
							
								
									78a8aec3e8
								
							
						
					
					
						commit
						4762e216d9
					
				| 
						 | 
					@ -0,0 +1,299 @@
 | 
				
			||||||
 | 
					local g = require("simple-graphics")
 | 
				
			||||||
 | 
					local mon = peripheral.wrap("monitor_24")
 | 
				
			||||||
 | 
					local W, H = mon.getSize()
 | 
				
			||||||
 | 
					local RUNNING = true -- Flag to indicate global program state.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Export flags
 | 
				
			||||||
 | 
					local EXPORTING = false -- Flag to indicate if we're exporting.
 | 
				
			||||||
 | 
					local EXPORT_SKIP = false -- Flag to indicate we should skip the current item.
 | 
				
			||||||
 | 
					local EXPORT_REPORT = false -- Flag to indicate we should report the current item as invalid.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function startsWith(str, start)
 | 
				
			||||||
 | 
					    return str:sub(1, #start) == start
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function getTotalItemCount(itemList)
 | 
				
			||||||
 | 
					    local total = 0
 | 
				
			||||||
 | 
					    for name, value in pairs(itemList) do
 | 
				
			||||||
 | 
					        if not startsWith(name, "__") then
 | 
				
			||||||
 | 
					            total = total + value
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local count = 1
 | 
				
			||||||
 | 
					    if itemList.__COUNT__ then count = itemList.__COUNT__ end
 | 
				
			||||||
 | 
					    return count * total
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function findItem(items, name)
 | 
				
			||||||
 | 
					    for _, item in pairs(items) do
 | 
				
			||||||
 | 
					        if item.name == name then return item end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    return nil
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function attemptItemExport(name, count)
 | 
				
			||||||
 | 
					    local P_NAME = "meBridge_4"
 | 
				
			||||||
 | 
					    local p = peripheral.wrap(P_NAME)
 | 
				
			||||||
 | 
					    if p == nil then
 | 
				
			||||||
 | 
					        return nil, "Missing peripheral "..P_NAME
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local func = function()
 | 
				
			||||||
 | 
					        return p.exportItem({name=name, count=count}, "south")
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local success, result = pcall(func)
 | 
				
			||||||
 | 
					    if success then
 | 
				
			||||||
 | 
					        return result
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        return nil, result
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function drawProgress(p)
 | 
				
			||||||
 | 
					    local discreteWidth = W - 2
 | 
				
			||||||
 | 
					    local filled = p * discreteWidth
 | 
				
			||||||
 | 
					    g.drawXLine(mon, 2, W-1, 5, colors.lightGray)
 | 
				
			||||||
 | 
					    g.drawXLine(mon, 2, 2+filled-1, 5, colors.green)
 | 
				
			||||||
 | 
					    local s = string.format("%.0f%%", p*100)
 | 
				
			||||||
 | 
					    for i = 1, #s do
 | 
				
			||||||
 | 
					        local x = 2+i
 | 
				
			||||||
 | 
					        local c = s:sub(i,i)
 | 
				
			||||||
 | 
					        local bg = colors.lightGray
 | 
				
			||||||
 | 
					        if x < filled then bg = colors.green end
 | 
				
			||||||
 | 
					        g.drawText(mon, x, 5, c, colors.white, bg)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function setProgressText(text)
 | 
				
			||||||
 | 
					    g.drawXLine(mon, 2, W-1, 6, colors.gray)
 | 
				
			||||||
 | 
					    if text ~= nil and #text > 0 then
 | 
				
			||||||
 | 
					        g.drawText(mon, 2, 6, text, colors.white, colors.gray)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function clearMainPanel()
 | 
				
			||||||
 | 
					    g.fillRect(mon, 1, 7, W, H-6, colors.black)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function clearExportPanel()
 | 
				
			||||||
 | 
					    g.fillRect(mon, 1, 7, W, H-9, colors.black)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function promptForUrlInput()
 | 
				
			||||||
 | 
					    clearMainPanel()
 | 
				
			||||||
 | 
					    drawProgress(0)
 | 
				
			||||||
 | 
					    setProgressText(nil)
 | 
				
			||||||
 | 
					    g.drawText(mon, 1, 8, "Please enter a schematic URL that", colors.white, colors.black)
 | 
				
			||||||
 | 
					    g.drawText(mon, 1, 9, "you've obtained from", colors.white, colors.black)
 | 
				
			||||||
 | 
					    g.drawText(mon, 1, 10, "schematics.andrewlalis.com in the", colors.white, colors.black)
 | 
				
			||||||
 | 
					    g.drawText(mon, 1, 11, "computer to your right to continue.", colors.white, colors.black)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g.clear(term, colors.black)
 | 
				
			||||||
 | 
					    g.drawText(term, 1, 1, "Paste your schematic URL here (CTRL+V): ", colors.white)
 | 
				
			||||||
 | 
					    g.drawText(term, 2, 2, "*Press enter without pasting to quit*", colors.gray)
 | 
				
			||||||
 | 
					    term.setCursorPos(1, 3)
 | 
				
			||||||
 | 
					    term.setTextColor(colors.lightGray)
 | 
				
			||||||
 | 
					    local link = io.read()
 | 
				
			||||||
 | 
					    if link == nil or #link == 0 then
 | 
				
			||||||
 | 
					        g.drawText(term, 1, 8, "No URL entered. Quitting.", colors.white)
 | 
				
			||||||
 | 
					        return nil
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        g.drawText(term, 1, 8, "URL was pasted. Please continue on the monitor.", colors.white)
 | 
				
			||||||
 | 
					        return link
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function fetchItemLists(url)
 | 
				
			||||||
 | 
					    local response = http.get(url)
 | 
				
			||||||
 | 
					    if response == nil then
 | 
				
			||||||
 | 
					        return nil, "HTTP request failed."
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    if response.getResponseCode() ~= 200 then
 | 
				
			||||||
 | 
					        return nil, "HTTP code " .. response.getResponseCode()
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local rawText = response.readAll()
 | 
				
			||||||
 | 
					    response.close()
 | 
				
			||||||
 | 
					    local itemLists, jsonErr = textutils.unserializeJSON(rawText)
 | 
				
			||||||
 | 
					    if not itemLists then
 | 
				
			||||||
 | 
					        return nil, "Failed to parse JSON: "..jsonErr
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    return itemLists
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function exportItem(name, count)
 | 
				
			||||||
 | 
					    clearExportPanel()
 | 
				
			||||||
 | 
					    g.drawText(mon, 1, 7, "Exporting "..count.." of", colors.white, colors.black)
 | 
				
			||||||
 | 
					    g.drawText(mon, 1, 8, name, colors.lime, colors.black)
 | 
				
			||||||
 | 
					    -- Check for flags and do different stuff if so.
 | 
				
			||||||
 | 
					    if EXPORT_REPORT then
 | 
				
			||||||
 | 
					        -- Report the item issue to the schematic site.
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 10, "Reporting item. Skipping to next one.", colors.orange, colors.black)
 | 
				
			||||||
 | 
					        os.sleep(2)
 | 
				
			||||||
 | 
					        return count
 | 
				
			||||||
 | 
					    elseif EXPORT_SKIP then
 | 
				
			||||||
 | 
					        -- Skip this item.
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 10, "Skipping this item.", colors.orange, colors.black)
 | 
				
			||||||
 | 
					        os.sleep(2)
 | 
				
			||||||
 | 
					        return count
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local me = peripheral.find("meBridge")
 | 
				
			||||||
 | 
					    if me == nil then
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 10, "Error: No \"meBridge\" peripheral.", colors.red, colors.black)
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 11, "Attach one please.", colors.red, colors.black)
 | 
				
			||||||
 | 
					        os.sleep(1)
 | 
				
			||||||
 | 
					        return 0
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local allItems, err = me.listItems()
 | 
				
			||||||
 | 
					    if allItems == nil or #allItems < 5 then
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 10, "Error: Couldn't list AE items.", colors.red, colors.black)
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 11, "Msg: " .. err, colors.red, colors.black)
 | 
				
			||||||
 | 
					        os.sleep(1)
 | 
				
			||||||
 | 
					        return 0
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local item = findItem(allItems, name)
 | 
				
			||||||
 | 
					    if item ~= nil and item.amount > 0 then
 | 
				
			||||||
 | 
					        local exported, err = attemptItemExport(name, count)
 | 
				
			||||||
 | 
					        if exported ~= nil then
 | 
				
			||||||
 | 
					            if exported == 0 then
 | 
				
			||||||
 | 
					                g.drawText(mon, 1, 10, "Exported 0 items. Make sure there is", colors.yellow, colors.black)
 | 
				
			||||||
 | 
					                g.drawText(mon, 1, 11, "space in the output container.", colors.yellow, colors.black)
 | 
				
			||||||
 | 
					                os.sleep(0.5)
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            return exported
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 10, "Transfer failed: " .. err, colors.red, colors.black)
 | 
				
			||||||
 | 
					        os.sleep(1)
 | 
				
			||||||
 | 
					        return 0
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 10, "Item isn't present in the AE system.", colors.yellow, colors.black)
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 11, "Please add some, craft, or skip.", colors.yellow, colors.black)
 | 
				
			||||||
 | 
					        os.sleep(1)
 | 
				
			||||||
 | 
					        return 0
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function exportSchematics(itemLists)
 | 
				
			||||||
 | 
					    -- First build a list of instances of schematics, each with a task list.
 | 
				
			||||||
 | 
					    local totalItemCount = 0
 | 
				
			||||||
 | 
					    local listInstances = {}
 | 
				
			||||||
 | 
					    for _, list in pairs(itemLists) do
 | 
				
			||||||
 | 
					        totalItemCount = totalItemCount + getTotalItemCount(list)
 | 
				
			||||||
 | 
					        for i = 1, list.__COUNT__ do
 | 
				
			||||||
 | 
					            local instance = {
 | 
				
			||||||
 | 
					                name = list.__NAME__,
 | 
				
			||||||
 | 
					                instanceNumber = i,
 | 
				
			||||||
 | 
					                countOfThisType = list.__COUNT__,
 | 
				
			||||||
 | 
					                tasks = {}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for name, amount in pairs(list) do
 | 
				
			||||||
 | 
					                if not startsWith(name, "__") then
 | 
				
			||||||
 | 
					                    table.insert(instance.tasks, {name=name, amount=amount})
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            table.insert(listInstances, instance)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    -- Now execute on that task list, quitting if the EXPORTING flag goes false.
 | 
				
			||||||
 | 
					    local totalItemsExported = 0
 | 
				
			||||||
 | 
					    local instanceIndex = 1
 | 
				
			||||||
 | 
					    while instanceIndex <= #listInstances and EXPORTING do
 | 
				
			||||||
 | 
					        local listInstance = listInstances[instanceIndex]
 | 
				
			||||||
 | 
					        setProgressText(listInstance.instanceNumber.."/"..listInstance.countOfThisType.." "..listInstance.name)
 | 
				
			||||||
 | 
					        local taskIndex = 1
 | 
				
			||||||
 | 
					        while taskIndex <= #listInstance.tasks and EXPORTING do
 | 
				
			||||||
 | 
					            local task = listInstance.tasks[taskIndex]
 | 
				
			||||||
 | 
					            local itemsExported = 0
 | 
				
			||||||
 | 
					            -- Reset item-specific control flags.
 | 
				
			||||||
 | 
					            EXPORT_REPORT = false
 | 
				
			||||||
 | 
					            EXPORT_SKIP = false
 | 
				
			||||||
 | 
					            while itemsExported < task.amount and EXPORTING do
 | 
				
			||||||
 | 
					                local exportedCount = exportItem(task.name, task.amount - itemsExported)
 | 
				
			||||||
 | 
					                itemsExported = itemsExported + exportedCount
 | 
				
			||||||
 | 
					                totalItemsExported = totalItemsExported + exportedCount
 | 
				
			||||||
 | 
					                drawProgress(totalItemsExported / totalItemCount)
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            taskIndex = taskIndex + 1
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        instanceIndex = instanceIndex + 1
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    -- Done! Show a small message, then set the EXPORTING flag to false.
 | 
				
			||||||
 | 
					    EXPORTING = false
 | 
				
			||||||
 | 
					    for i = 1, 3 do -- Queue up some no-op touch events make sure the event handler quits.
 | 
				
			||||||
 | 
					        os.queueEvent("monitor_touch", "monitor_24", 1, 1)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    g.drawText(mon, 1, H-4, "Export complete!", colors.lime, colors.black)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function handleExportEvents()
 | 
				
			||||||
 | 
					    while EXPORTING do
 | 
				
			||||||
 | 
					        local event, monName, x, y = os.pullEvent("monitor_touch")
 | 
				
			||||||
 | 
					        if monName == "monitor_24" and y >= H-2 then
 | 
				
			||||||
 | 
					            if x >= 27 then
 | 
				
			||||||
 | 
					                EXPORTING = false
 | 
				
			||||||
 | 
					            elseif x >= 14 then
 | 
				
			||||||
 | 
					                EXPORT_REPORT = true
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                EXPORT_SKIP = true
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function handleSchematicUrl(url)
 | 
				
			||||||
 | 
					    clearMainPanel()
 | 
				
			||||||
 | 
					    if url == nil then
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 8, "No URL was entered. Quitting.", colors.white, colors.black)
 | 
				
			||||||
 | 
					        RUNNING = false
 | 
				
			||||||
 | 
					        os.sleep(2)
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    g.drawText(mon, 1, 8, "Fetching item lists from URL...", colors.lightGray, colors.black)
 | 
				
			||||||
 | 
					    local itemLists, err = fetchItemLists(url)
 | 
				
			||||||
 | 
					    if not itemLists then
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 9, "Failed to fetch item lists.", colors.red, colors.black)
 | 
				
			||||||
 | 
					        g.drawText(mon, 1, 10, err, colors.red, colors.black)
 | 
				
			||||||
 | 
					        os.sleep(3)
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    g.drawText(mon, 1, 9, "Got lists for "..(#itemLists).." schematics.", colors.white, colors.black)
 | 
				
			||||||
 | 
					    local y = 10
 | 
				
			||||||
 | 
					    for i, itemList in pairs(itemLists) do
 | 
				
			||||||
 | 
					        local count = itemList.__COUNT__
 | 
				
			||||||
 | 
					        local name = itemList.__NAME__
 | 
				
			||||||
 | 
					        g.drawText(mon, 2, y, count.."x "..name, colors.white, colors.black)
 | 
				
			||||||
 | 
					        y = y+1
 | 
				
			||||||
 | 
					        if y == H and i < #itemLists then
 | 
				
			||||||
 | 
					            local numRemaining = #itemLists - i
 | 
				
			||||||
 | 
					            g.drawText(mon, 2, y, "... and "..numRemaining.." more.")
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    os.sleep(3)
 | 
				
			||||||
 | 
					    -- Draw control buttons.
 | 
				
			||||||
 | 
					    g.fillRect(mon, 1, H-2, 13, 3, colors.blue)
 | 
				
			||||||
 | 
					    g.drawText(mon, 5, H-1, "Skip", colors.white)
 | 
				
			||||||
 | 
					    g.fillRect(mon, 14, H-2, 13, 3, colors.yellow)
 | 
				
			||||||
 | 
					    g.drawText(mon, 14, H-1, "Report Error", colors.lightBlue)
 | 
				
			||||||
 | 
					    g.fillRect(mon, 27, H-2, 13, 3, colors.red)
 | 
				
			||||||
 | 
					    g.drawText(mon, 31, H-1, "Quit", colors.white)
 | 
				
			||||||
 | 
					    EXPORTING = true
 | 
				
			||||||
 | 
					    local fExport = function() exportSchematics(itemLists) end
 | 
				
			||||||
 | 
					    local fHandleEvents = function() handleExportEvents() end
 | 
				
			||||||
 | 
					    parallel.waitForAll(fExport, fHandleEvents)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- MAIN SCRIPT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.clear(mon, colors.black)
 | 
				
			||||||
 | 
					g.fillRect(mon, 1, 1, W, 3, colors.yellow)
 | 
				
			||||||
 | 
					g.drawTextCenter(mon, W/2, 2, "Schematic Exporter", colors.black)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.fillRect(mon, 1, 4, W, 3, colors.gray)
 | 
				
			||||||
 | 
					g.drawText(mon, 2, 4, "Progress", colors.white)
 | 
				
			||||||
 | 
					g.drawXLine(mon, 2, W-1, 5, colors.lightGray)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					while RUNNING do
 | 
				
			||||||
 | 
					    local url = promptForUrlInput()
 | 
				
			||||||
 | 
					    handleSchematicUrl(url)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@ void startServer() {
 | 
				
			||||||
	PathDelegatingHandler handler = new PathDelegatingHandler();
 | 
						PathDelegatingHandler handler = new PathDelegatingHandler();
 | 
				
			||||||
	handler.addMapping("POST", "/extracts", &handleExtract);
 | 
						handler.addMapping("POST", "/extracts", &handleExtract);
 | 
				
			||||||
	handler.addMapping("GET", "/extracts/{extractId}", &getExtract);
 | 
						handler.addMapping("GET", "/extracts/{extractId}", &getExtract);
 | 
				
			||||||
 | 
						handler.addMapping("POST", "/item-reports", &handleItemReport);
 | 
				
			||||||
	handler.addMapping("GET", "/status", (ref HttpRequestContext ctx) {
 | 
						handler.addMapping("GET", "/status", (ref HttpRequestContext ctx) {
 | 
				
			||||||
		ctx.response.setStatus(HttpStatus.OK);
 | 
							ctx.response.setStatus(HttpStatus.OK);
 | 
				
			||||||
		ctx.response.writeBodyString("online");
 | 
							ctx.response.writeBodyString("online");
 | 
				
			||||||
| 
						 | 
					@ -43,3 +44,13 @@ private void getExtract(ref HttpRequestContext ctx) {
 | 
				
			||||||
	const extractFile = buildPath(EXTRACTS_DIR, extractId ~ ".json");
 | 
						const extractFile = buildPath(EXTRACTS_DIR, extractId ~ ".json");
 | 
				
			||||||
	fileResponse(ctx.response, extractFile, "application/json");
 | 
						fileResponse(ctx.response, extractFile, "application/json");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private void handleItemReport(ref HttpRequestContext ctx) {
 | 
				
			||||||
 | 
						import std.string : strip;
 | 
				
			||||||
 | 
						import std.stdio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						string itemName = ctx.request.readBodyAsString().strip();
 | 
				
			||||||
 | 
						File f = File("item-reports.txt", "a");
 | 
				
			||||||
 | 
						f.writeln(itemName);
 | 
				
			||||||
 | 
						f.close();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue